A ext login

This commit is contained in:
zhenorzz 2022-10-11 10:13:17 +08:00
parent bcd20d37b1
commit 1372f06931
11 changed files with 159 additions and 15 deletions

View File

@ -5,13 +5,16 @@
package controller package controller
import ( import (
"crypto/md5"
"database/sql" "database/sql"
"encoding/hex"
"fmt" "fmt"
"github.com/go-ldap/ldap/v3" "github.com/go-ldap/ldap/v3"
"github.com/zhenorzz/goploy/middleware" "github.com/zhenorzz/goploy/middleware"
"github.com/zhenorzz/goploy/permission" "github.com/zhenorzz/goploy/permission"
"github.com/zhenorzz/goploy/response" "github.com/zhenorzz/goploy/response"
"net/http" "net/http"
"strconv"
"time" "time"
"github.com/zhenorzz/goploy/config" "github.com/zhenorzz/goploy/config"
@ -24,6 +27,7 @@ type User Controller
func (u User) Routes() []core.Route { func (u User) Routes() []core.Route {
return []core.Route{ return []core.Route{
core.NewWhiteRoute("/user/login", http.MethodPost, u.Login).LogFunc(middleware.AddLoginLog), core.NewWhiteRoute("/user/login", http.MethodPost, u.Login).LogFunc(middleware.AddLoginLog),
core.NewWhiteRoute("/user/extLogin", http.MethodPost, u.ExtLogin).LogFunc(middleware.AddLoginLog),
core.NewRoute("/user/info", http.MethodGet, u.Info), core.NewRoute("/user/info", http.MethodGet, u.Info),
core.NewRoute("/user/changePassword", http.MethodPut, u.ChangePassword), core.NewRoute("/user/changePassword", http.MethodPut, u.ChangePassword),
core.NewRoute("/user/getList", http.MethodGet, u.GetList).Permissions(permission.ShowMemberPage), core.NewRoute("/user/getList", http.MethodGet, u.GetList).Permissions(permission.ShowMemberPage),
@ -36,7 +40,7 @@ func (u User) Routes() []core.Route {
func (User) Login(gp *core.Goploy) core.Response { func (User) Login(gp *core.Goploy) core.Response {
type ReqData struct { type ReqData struct {
Account string `json:"account" validate:"min=5,max=25"` Account string `json:"account" validate:"min=1,max=25"`
Password string `json:"password" validate:"password"` Password string `json:"password" validate:"password"`
} }
var reqData ReqData var reqData ReqData
@ -126,6 +130,69 @@ func (User) Login(gp *core.Goploy) core.Response {
} }
} }
func (User) ExtLogin(gp *core.Goploy) core.Response {
type ReqData struct {
Account string `json:"account" validate:"min=1,max=25"`
Time int64 `json:"time"`
Token string `json:"token" validate:"len=32"`
}
var reqData ReqData
if err := decodeJson(gp.Body, &reqData); err != nil {
return response.JSON{Code: response.IllegalParam, Message: err.Error()}
}
if time.Now().Unix() > reqData.Time+30 {
return response.JSON{Code: response.IllegalParam, Message: "request time expired"}
}
h := md5.New()
h.Write([]byte(reqData.Account + config.Toml.JWT.Key + strconv.FormatInt(reqData.Time, 10)))
signedToken := hex.EncodeToString(h.Sum(nil))
if signedToken != reqData.Token {
return response.JSON{Code: response.IllegalParam, Message: "sign error"}
}
userData, err := model.User{Account: reqData.Account}.GetDataByAccount()
if err != nil {
return response.JSON{Code: response.Error, Message: err.Error()}
}
if userData.State == model.Disable {
return response.JSON{Code: response.AccountDisabled, Message: "Account is disabled"}
}
namespaceList, err := model.Namespace{UserID: userData.ID}.GetAllByUserID()
if err != nil {
return response.JSON{Code: response.Error, Message: err.Error()}
} else if len(namespaceList) == 0 {
return response.JSON{Code: response.Error, Message: "No space assigned, please contact the administrator"}
}
token, err := userData.CreateToken()
if err != nil {
return response.JSON{Code: response.Error, Message: err.Error()}
}
_ = model.User{ID: userData.ID, LastLoginTime: time.Now().Format("20060102150405")}.UpdateLastLoginTime()
cookie := http.Cookie{
Name: config.Toml.Cookie.Name,
Value: token,
Path: "/",
MaxAge: config.Toml.Cookie.Expire,
HttpOnly: true,
}
http.SetCookie(gp.ResponseWriter, &cookie)
return response.JSON{
Data: struct {
Token string `json:"token"`
NamespaceList model.Namespaces `json:"namespaceList"`
}{Token: token, NamespaceList: namespaceList},
}
}
func (User) Info(gp *core.Goploy) core.Response { func (User) Info(gp *core.Goploy) core.Response {
type RespData struct { type RespData struct {
UserInfo struct { UserInfo struct {
@ -180,7 +247,7 @@ func (User) GetOption(*core.Goploy) core.Response {
func (User) Add(gp *core.Goploy) core.Response { func (User) Add(gp *core.Goploy) core.Response {
type ReqData struct { type ReqData struct {
Account string `json:"account" validate:"min=5,max=25"` Account string `json:"account" validate:"min=1,max=25"`
Password string `json:"password" validate:"omitempty,password"` Password string `json:"password" validate:"omitempty,password"`
Name string `json:"name" validate:"required"` Name string `json:"name" validate:"required"`
Contact string `json:"contact" validate:"omitempty,len=11,numeric"` Contact string `json:"contact" validate:"omitempty,len=11,numeric"`

View File

@ -42,7 +42,7 @@ type RouteApi interface {
} }
type Response interface { type Response interface {
Write(http.ResponseWriter) error Write(http.ResponseWriter, *http.Request) error
} }
type Route struct { type Route struct {
@ -149,7 +149,7 @@ func (rt Router) ServeHTTP(w http.ResponseWriter, r *http.Request) {
} }
_, resp := rt.doRequest(w, r) _, resp := rt.doRequest(w, r)
if err := resp.Write(w); err != nil { if err := resp.Write(w, r); err != nil {
Log(ERROR, err.Error()) Log(ERROR, err.Error())
} }
return return

View File

@ -10,5 +10,4 @@ import (
type Empty struct{} type Empty struct{}
//JSON response func (Empty) Write(http.ResponseWriter, *http.Request) error { return nil }
func (Empty) Write(http.ResponseWriter) error { return nil }

View File

@ -15,8 +15,7 @@ type File struct {
Filename string Filename string
} }
//JSON response func (f File) Write(w http.ResponseWriter, _ *http.Request) error {
func (f File) Write(w http.ResponseWriter) error {
file, err := os.Open(f.Filename) file, err := os.Open(f.Filename)
if err != nil { if err != nil {
return err return err

View File

@ -15,7 +15,6 @@ type JSON struct {
Data interface{} `json:"data"` Data interface{} `json:"data"`
} }
// response code
const ( const (
Pass = 0 Pass = 0
Deny = 1 Deny = 1
@ -28,6 +27,6 @@ const (
) )
//JSON response //JSON response
func (j JSON) Write(w http.ResponseWriter) error { func (j JSON) Write(w http.ResponseWriter, _ *http.Request) error {
return json.NewEncoder(w).Encode(j) return json.NewEncoder(w).Encode(j)
} }

19
response/Redirect.go Normal file
View File

@ -0,0 +1,19 @@
// Copyright 2022 The Goploy Authors. All rights reserved.
// Use of this source code is governed by a GPLv3-style
// license that can be found in the LICENSE file.
package response
import (
"net/http"
)
type Redirect struct {
URL string
Code int
}
func (rdr Redirect) Write(w http.ResponseWriter, r *http.Request) error {
http.Redirect(w, r, rdr.URL, rdr.Code)
return nil
}

View File

@ -18,8 +18,7 @@ type SftpFile struct {
Disposition string // attachment | inline Disposition string // attachment | inline
} }
//JSON response func (sf SftpFile) Write(w http.ResponseWriter, _ *http.Request) error {
func (sf SftpFile) Write(w http.ResponseWriter) error {
defer sf.Client.Close() defer sf.Client.Close()
sftpClient, err := sftp.NewClient(sf.Client) sftpClient, err := sftp.NewClient(sf.Client)

View File

@ -29,6 +29,23 @@ export class Login extends Request {
} }
} }
export class extLogin extends Request {
readonly url = '/user/extLogin'
readonly method = 'post'
public param: {
account: string
token: string
time: number
}
public declare datagram: {
namespaceList: { id: number; name: string; roleId: number }[]
}
constructor(param: extLogin['param']) {
super()
this.param = param
}
}
export class Info extends Request { export class Info extends Request {
readonly url = '/user/info' readonly url = '/user/info'
readonly method = 'get' readonly method = 'get'

View File

@ -1,7 +1,7 @@
import { Module, MutationTree, ActionTree } from 'vuex' import { Module, MutationTree, ActionTree } from 'vuex'
import { UserState } from './types' import { UserState } from './types'
import { RootState } from '../../types' import { RootState } from '../../types'
import { Login, Info } from '@/api/user' import { Login, extLogin, Info } from '@/api/user'
import { setLogin, logout } from '@/utils/auth' import { setLogin, logout } from '@/utils/auth'
import { getNamespaceId, setNamespace } from '@/utils/namespace' import { getNamespaceId, setNamespace } from '@/utils/namespace'
import { resetRouter } from '@/router' import { resetRouter } from '@/router'
@ -51,6 +51,27 @@ const actions: ActionTree<UserState, RootState> = {
}) })
}, },
// user ext login
extLogin(_, userInfo) {
return new Promise((resolve, reject) => {
new extLogin(userInfo)
.request()
.then((response) => {
const { data } = response
if (!getNamespaceId()) {
const namespace = data.namespaceList[data.namespaceList.length - 1]
setNamespace(namespace)
}
setLogin('ok')
resolve(response)
})
.catch((error) => {
reject(error)
})
})
},
// get user info // get user info
getInfo({ commit }) { getInfo({ commit }) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {

View File

@ -15,7 +15,7 @@ export function isExternal(path: string): boolean {
* @returns {Boolean} * @returns {Boolean}
*/ */
export function validUsername(str: string): boolean { export function validUsername(str: string): boolean {
return str.trim().length >= 5 return str.trim().length >= 1
} }
export function validPassword(str: string): boolean { export function validPassword(str: string): boolean {
@ -29,6 +29,7 @@ export function validPassword(str: string): boolean {
/* 合法uri*/ /* 合法uri*/
export function validateURL(textval: string): boolean { export function validateURL(textval: string): boolean {
const urlregex = /^(https?|ftp):\/\/([a-zA-Z0-9.-]+(:[a-zA-Z0-9.&%$-]+)*@)*((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.(com|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2}))(:[0-9]+)*(\/($|[a-zA-Z0-9.,?'\\+&%$#=~_-]+))*$/ const urlregex =
/^(https?|ftp):\/\/([a-zA-Z0-9.-]+(:[a-zA-Z0-9.&%$-]+)*@)*((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.(com|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2}))(:[0-9]+)*(\/($|[a-zA-Z0-9.,?'\\+&%$#=~_-]+))*$/
return urlregex.test(textval) return urlregex.test(textval)
} }

View File

@ -117,6 +117,14 @@ watch(
useRoute(), useRoute(),
(route) => { (route) => {
redirect.value = route.query?.redirect redirect.value = route.query?.redirect
console.log(route.query)
if (route.query['account'] && route.query['time'] && route.query['token']) {
handleExtLogin(
route.query['account'] as string,
Number(route.query['time']),
route.query['token'] as string
)
}
}, },
{ immediate: true } { immediate: true }
) )
@ -133,6 +141,21 @@ function showPwd() {
}) })
} }
function handleExtLogin(account: string, time: number, token: string) {
store
.dispatch('user/extLogin', { account, time, token })
.then(() => {
router.push({
path: redirect.value || '/',
query: redirect.value ? param2Obj(redirect.value) : {},
})
loading.value = false
})
.catch(() => {
loading.value = false
})
}
function handleLogin() { function handleLogin() {
form.value?.validate((valid) => { form.value?.validate((valid) => {
if (valid) { if (valid) {