diff --git a/docs/guide-zh-CN/addon-helper.md b/docs/guide-zh-CN/addon-helper.md index 6c06d57..7466111 100644 --- a/docs/guide-zh-CN/addon-helper.md +++ b/docs/guide-zh-CN/addon-helper.md @@ -91,18 +91,12 @@ import ( ) // RouterPrefix 路由前缀 -// 最终效果:/应用名称/插件模块名称/xxx/xxx +// 最终效果:/应用名称/插件模块名称/xxx/xxx。如果你不喜欢现在的路由风格,可以自行调整 func RouterPrefix(ctx context.Context, app, name string) string { var prefix = "/" - switch app { - case consts.AppAdmin: - prefix = g.Cfg().MustGet(ctx, "router.admin.prefix", "/admin").String() - case consts.AppApi: - prefix = g.Cfg().MustGet(ctx, "router.api.prefix", "/api").String() - case consts.AppHome: - prefix = g.Cfg().MustGet(ctx, "router.home.prefix", "/home").String() - case consts.AppWebSocket: - prefix = g.Cfg().MustGet(ctx, "router.ws.prefix", "/socket").String() + + if app != "" { + prefix = g.Cfg().MustGet(ctx, "router."+app+".prefix", "/"+app+"").String() } return prefix + "/" + name diff --git a/docs/guide-zh-CN/start-update-log.md b/docs/guide-zh-CN/start-update-log.md index b55bfb4..0270a5a 100644 --- a/docs/guide-zh-CN/start-update-log.md +++ b/docs/guide-zh-CN/start-update-log.md @@ -11,6 +11,13 @@ > 如果升级(覆盖)代码后打开会出现 sql 报错, 请检查更新的数据库格式或自行调整 + +### v2.6.10 +updated 2023.05.12 +- 增加:增加jwt+缓存驱动实现的令牌认证,支持自动续约、可控过期失效策略 +- 移除:移除旧jwt功能库 +- 移除:移除`decimal`、`mahonia` package + ### v2.6.7 updated 2023.05.10 - 增加:增加支付网关:集成支付宝、微信支付、QQ支付,可通过后台直接配置参数 diff --git a/docs/guide-zh-CN/sys-library.md b/docs/guide-zh-CN/sys-library.md index dfee566..e3cd0df 100644 --- a/docs/guide-zh-CN/sys-library.md +++ b/docs/guide-zh-CN/sys-library.md @@ -3,8 +3,8 @@ 目录 - 缓存驱动 -- 请求上下文(待写) -- JWT(待写) +- 请求上下文 +- JWT - 地理定位(待写) - 通知(待写) @@ -87,8 +87,68 @@ func test(ctx context.Context) { ``` ### JWT + +- 基于jwt+缓存驱动实现的用户登录令牌功能,支持自动续约,解决了jwt服务端无法退出问题和jwt令牌无法主动失效问题 + +#### 配置示例 +```yaml +# 登录令牌 +token: + secretKey: "hotgo123" # 令牌加密秘钥,考虑安全问题生产环境中请修改默认值 + expires: 604800 # 令牌有效期,单位:秒。默认7天 + autoRefresh: true # 是否开启自动刷新过期时间, false|true 默认为true + refreshInterval: 86400 # 刷新间隔,单位:秒。必须小于expires,否则无法触发。默认1天内只允许刷新一次 + maxRefreshTimes: 30 # 最大允许刷新次数,-1不限制。默认30次 + multiLogin: true # 是否允许多端登录, false|true 默认为true + +``` + ```go -// 待写 +package admin + +import ( + "fmt" + "context" + "hotgo/internal/library/token" + "hotgo/internal/model" +) + + +func test(ctx context.Context) { + // 登录 + user := &model.Identity{ + Id: mb.Id, + Pid: mb.Pid, + DeptId: mb.DeptId, + RoleId: ro.Id, + RoleKey: ro.Key, + Username: mb.Username, + RealName: mb.RealName, + Avatar: mb.Avatar, + Email: mb.Email, + Mobile: mb.Mobile, + App: consts.AppAdmin, + LoginAt: gtime.Now(), + } + + loginToken, expires, err := token.Login(ctx, user) + if err != nil { + return nil, err + } + + // gf请求对象 + r := *ghttp.Request + + // 获取登录用户信息 + user, err := token.ParseLoginUser(r) + if err != nil { + return + } + + // 注销登录 + err = token.Logout(r) +} + ``` ### 地理定位 diff --git a/server/addons/hgexample/router/websocket.go b/server/addons/hgexample/router/websocket.go index facdcc2..a5eba36 100644 --- a/server/addons/hgexample/router/websocket.go +++ b/server/addons/hgexample/router/websocket.go @@ -25,7 +25,7 @@ func WebSocket(ctx context.Context, group *ghttp.RouterGroup) { websocket.Index, ) // ws连接中间件 - group.Middleware(service.Middleware().WebSocketToken) + group.Middleware(service.Middleware().WebSocketAuth) group.Bind( // 需要验证的路由 // .. diff --git a/server/api/admin/common/site.go b/server/api/admin/common/site.go index e4517d0..b8052a1 100644 --- a/server/api/admin/common/site.go +++ b/server/api/admin/common/site.go @@ -3,7 +3,6 @@ // @Copyright Copyright (c) 2023 HotGo CLI // @Author Ms <133814250@qq.com> // @License https://github.com/bufanyun/hotgo/blob/master/LICENSE -// package common import ( @@ -13,7 +12,7 @@ import ( // LoginLogoutReq 注销登录 type LoginLogoutReq struct { - g.Meta `path:"/site/logout" method:"get" tags:"后台基础" summary:"注销登录"` + g.Meta `path:"/site/logout" method:"post" tags:"后台基础" summary:"注销登录"` } type LoginLogoutRes struct{} @@ -21,7 +20,6 @@ type LoginLogoutRes struct{} type LoginCaptchaReq struct { g.Meta `path:"/site/captcha" method:"get" tags:"后台基础" summary:"获取登录验证码"` } - type LoginCaptchaRes struct { Cid string `json:"cid" dc:"验证码ID"` Base64 string `json:"base64" dc:"验证码"` diff --git a/server/api/admin/monitor/monitor.go b/server/api/admin/monitor/monitor.go index 4ee9d53..cf28a64 100644 --- a/server/api/admin/monitor/monitor.go +++ b/server/api/admin/monitor/monitor.go @@ -3,7 +3,6 @@ // @Copyright Copyright (c) 2023 HotGo CLI // @Author Ms <133814250@qq.com> // @License https://github.com/bufanyun/hotgo/blob/master/LICENSE -// package monitor import ( @@ -47,13 +46,12 @@ type OnlineModel struct { Addr string `json:"addr"` // 客户端地址 Os string `json:"os"` // 客户端系统名称 Browser string `json:"browser"` // 浏览器 - FirstTime uint64 `json:"firstTime"` // 首次连接时间 + FirstTime int64 `json:"firstTime"` // 首次连接时间 HeartbeatTime uint64 `json:"heartbeatTime"` // 用户上次心跳时间 App string `json:"app"` // 应用名称 UserId int64 `json:"userId"` // 用户ID Username string `json:"username"` // 用户名 Avatar string `json:"avatar"` // 头像 - ExpTime int64 `json:"expTime"` // 过期时间 } type OnlineModels []*OnlineModel diff --git a/server/go.mod b/server/go.mod index 08b471f..6d7b519 100644 --- a/server/go.mod +++ b/server/go.mod @@ -10,7 +10,6 @@ require ( github.com/alibabacloud-go/tea-utils/v2 v2.0.1 github.com/aliyun/aliyun-oss-go-sdk v2.2.6+incompatible github.com/apache/rocketmq-client-go/v2 v2.1.0 - github.com/axgle/mahonia v0.0.0-20180208002826-3358181d7394 github.com/casbin/casbin/v2 v2.55.0 github.com/dgrijalva/jwt-go v3.2.0+incompatible github.com/forgoer/openssl v1.4.0 @@ -18,13 +17,13 @@ require ( github.com/gogf/gf/contrib/drivers/mysql/v2 v2.4.1 github.com/gogf/gf/contrib/nosql/redis/v2 v2.4.1 github.com/gogf/gf/v2 v2.4.1 + github.com/golang-jwt/jwt/v5 v5.0.0 github.com/gorilla/websocket v1.5.0 github.com/kayon/iploc v0.0.0-20200312105652-bda3e968a794 github.com/mojocn/base64Captcha v1.3.5 github.com/olekukonko/tablewriter v0.0.5 github.com/qiniu/go-sdk/v7 v7.14.0 github.com/shirou/gopsutil/v3 v3.23.3 - github.com/shopspring/decimal v1.3.1 github.com/silenceper/wechat/v2 v2.1.4 github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.633 github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/sms v1.0.633 diff --git a/server/go.sum b/server/go.sum index fe92d6f..9420885 100644 --- a/server/go.sum +++ b/server/go.sum @@ -81,8 +81,6 @@ github.com/aliyun/credentials-go v1.1.2 h1:qU1vwGIBb3UJ8BwunHDRFtAhS6jnQLnde/yk0 github.com/aliyun/credentials-go v1.1.2/go.mod h1:ozcZaMR5kLM7pwtCMEpVmQ242suV6qTJya2bDq4X1Tw= github.com/apache/rocketmq-client-go/v2 v2.1.0 h1:3eABKfxc1WmS2lLTTbKMe1gZfZV6u1Sx9orFnOfABV0= github.com/apache/rocketmq-client-go/v2 v2.1.0/go.mod h1:oEZKFDvS7sz/RWU0839+dQBupazyBV7WX5cP6nrio0Q= -github.com/axgle/mahonia v0.0.0-20180208002826-3358181d7394 h1:OYA+5W64v3OgClL+IrOD63t4i/RW7RqrAVl9LTZ9UqQ= -github.com/axgle/mahonia v0.0.0-20180208002826-3358181d7394/go.mod h1:Q8n74mJTIgjX4RBBcHnJ05h//6/k6foqmgE45jTQtxg= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= @@ -179,6 +177,8 @@ github.com/gogf/gf/contrib/nosql/redis/v2 v2.4.1/go.mod h1:0t7pBtXdfuemskzkdxyC2 github.com/gogf/gf/v2 v2.4.1 h1:snsuvDhNFiRoAuWBbKfIIng0KyMaRA87Qr03GLir5j8= github.com/gogf/gf/v2 v2.4.1/go.mod h1:tsbmtwcAl2chcYoq/fP9W2FZf06aw4i89X34nbSHo9Y= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/golang-jwt/jwt/v5 v5.0.0 h1:1n1XNM9hk7O9mnQoNBGolZvzebBQ7p93ULHRc28XJUE= +github.com/golang-jwt/jwt/v5 v5.0.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= @@ -418,8 +418,6 @@ github.com/shoenig/go-m1cpu v0.1.4 h1:SZPIgRM2sEF9NJy50mRHu9PKGwxyyTTJIWvCtgVboz github.com/shoenig/go-m1cpu v0.1.4/go.mod h1:Wwvst4LR89UxjeFtLRMrpgRiyY4xPsejnVZym39dbAQ= github.com/shoenig/test v0.6.3 h1:GVXWJFk9PiOjN0KoJ7VrJGH6uLPnqxR7/fe3HUPfE0c= github.com/shoenig/test v0.6.3/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k= -github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8= -github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/silenceper/wechat/v2 v2.1.4 h1:X+G9C/EiBET5AK0zhrflX3ESCP/yxhJUvoRoSXHm0js= github.com/silenceper/wechat/v2 v2.1.4/go.mod h1:F0PKqImb15THnwoqRNrZO1z3vpwyWuiHr5zzfnjdECY= diff --git a/server/internal/consts/cache.go b/server/internal/consts/cache.go index 34f9f76..499b7f8 100644 --- a/server/internal/consts/cache.go +++ b/server/internal/consts/cache.go @@ -7,6 +7,6 @@ package consts // cache const ( - CacheJwtToken = "jwt_token:" // JWT-token - CacheJwtUserBind = "jwt_user_bind:" // JWT-用户身份绑定 + CacheToken = "token" // 登录token + CacheTokenBind = "token_bind" // 登录用户身份绑定 ) diff --git a/server/internal/consts/error.go b/server/internal/consts/error.go index e78bf59..fa392ed 100644 --- a/server/internal/consts/error.go +++ b/server/internal/consts/error.go @@ -5,7 +5,9 @@ // @License https://github.com/bufanyun/hotgo/blob/master/LICENSE package consts -import "github.com/gogf/gf/v2/text/gstr" +import ( + "github.com/gogf/gf/v2/text/gstr" +) // 错误解释 const ( diff --git a/server/internal/consts/version.go b/server/internal/consts/version.go index 2223328..ff737c9 100644 --- a/server/internal/consts/version.go +++ b/server/internal/consts/version.go @@ -7,5 +7,5 @@ package consts // VersionApp HotGo版本 const ( - VersionApp = "2.6.7" + VersionApp = "2.6.10" ) diff --git a/server/internal/controller/admin/admin/monitor.go b/server/internal/controller/admin/admin/monitor.go index 6193f0d..02df44a 100644 --- a/server/internal/controller/admin/admin/monitor.go +++ b/server/internal/controller/admin/admin/monitor.go @@ -3,13 +3,13 @@ // @Copyright Copyright (c) 2023 HotGo CLI // @Author Ms <133814250@qq.com> // @License https://github.com/bufanyun/hotgo/blob/master/LICENSE -// package admin import ( "context" "github.com/gogf/gf/v2/errors/gerror" "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/os/gtime" "github.com/gogf/gf/v2/text/gstr" "hotgo/api/admin/monitor" "hotgo/internal/consts" @@ -79,13 +79,12 @@ func (c *cMonitor) OnlineList(ctx context.Context, req *monitor.OnlineListReq) ( Addr: conn.Addr, Os: useragent.GetOs(conn.UserAgent), Browser: useragent.GetBrowser(conn.UserAgent), - FirstTime: conn.FirstTime, + FirstTime: gtime.New(conn.User.LoginAt).Unix(), HeartbeatTime: conn.HeartbeatTime, App: conn.User.App, UserId: conn.User.Id, Username: conn.User.Username, Avatar: conn.User.Avatar, - ExpTime: conn.User.Exp, }) } diff --git a/server/internal/controller/admin/common/site.go b/server/internal/controller/admin/common/site.go index 44fc1a3..22ec469 100644 --- a/server/internal/controller/admin/common/site.go +++ b/server/internal/controller/admin/common/site.go @@ -7,16 +7,14 @@ package common import ( "context" - "github.com/gogf/gf/v2/crypto/gmd5" "github.com/gogf/gf/v2/errors/gerror" "github.com/gogf/gf/v2/net/ghttp" "github.com/gogf/gf/v2/text/gstr" "github.com/gogf/gf/v2/util/gconv" "hotgo/api/admin/common" "hotgo/internal/consts" - "hotgo/internal/library/cache" "hotgo/internal/library/captcha" - "hotgo/internal/library/jwt" + "hotgo/internal/library/token" "hotgo/internal/model/input/adminin" "hotgo/internal/model/input/sysin" "hotgo/internal/service" @@ -112,13 +110,6 @@ func (c *cSite) Login(ctx context.Context, req *common.LoginReq) (res *common.Lo // Logout 注销登录 func (c *cSite) Logout(ctx context.Context, req *common.LoginLogoutReq) (res *common.LoginLogoutRes, err error) { - token := consts.CacheJwtToken + gmd5.MustEncryptString(jwt.GetAuthorization(ghttp.RequestFromCtx(ctx))) - if len(token) == 0 { - err = gerror.New("当前用户未登录!") - return - } - - // 删除登录token - _, err = cache.Instance().Remove(ctx, token) + err = token.Logout(ghttp.RequestFromCtx(ctx)) return } diff --git a/server/internal/dao/internal/admin_member.go b/server/internal/dao/internal/admin_member.go index 327e6f3..3ed51dd 100644 --- a/server/internal/dao/internal/admin_member.go +++ b/server/internal/dao/internal/admin_member.go @@ -27,7 +27,6 @@ type AdminMemberColumns struct { Username string // 帐号 PasswordHash string // 密码 Salt string // 密码盐 - AuthKey string // 授权令牌 PasswordResetToken string // 密码重置令牌 Integral string // 积分 Balance string // 余额 @@ -59,7 +58,6 @@ var adminMemberColumns = AdminMemberColumns{ Username: "username", PasswordHash: "password_hash", Salt: "salt", - AuthKey: "auth_key", PasswordResetToken: "password_reset_token", Integral: "integral", Balance: "balance", diff --git a/server/internal/dao/internal/sys_log.go b/server/internal/dao/internal/sys_log.go index a977276..4c76de6 100644 --- a/server/internal/dao/internal/sys_log.go +++ b/server/internal/dao/internal/sys_log.go @@ -35,7 +35,7 @@ type SysLogColumns struct { ProvinceId string // 省编码 CityId string // 市编码 ErrorCode string // 报错code - ErrorMsg string // 报错信息 + ErrorMsg string // 对外错误提示 ErrorData string // 报错日志 UserAgent string // UA信息 TakeUpTime string // 请求耗时 diff --git a/server/internal/library/addons/addons.go b/server/internal/library/addons/addons.go index 7730bbc..cacd102 100644 --- a/server/internal/library/addons/addons.go +++ b/server/internal/library/addons/addons.go @@ -23,15 +23,9 @@ func Tpl(name, tpl string) string { // 最终效果:/应用名称/插件模块名称/xxx/xxx。如果你不喜欢现在的路由风格,可以自行调整 func RouterPrefix(ctx context.Context, app, name string) string { var prefix = "/" - switch app { - case consts.AppAdmin: - prefix = g.Cfg().MustGet(ctx, "router.admin.prefix", "/admin").String() - case consts.AppApi: - prefix = g.Cfg().MustGet(ctx, "router.api.prefix", "/api").String() - case consts.AppHome: - prefix = g.Cfg().MustGet(ctx, "router.home.prefix", "/home").String() - case consts.AppWebSocket: - prefix = g.Cfg().MustGet(ctx, "router.ws.prefix", "/socket").String() + + if app != "" { + prefix = g.Cfg().MustGet(ctx, "router."+app+".prefix", "/"+app+"").String() } return prefix + "/" + name diff --git a/server/internal/library/jwt/jwt.go b/server/internal/library/jwt/jwt.go deleted file mode 100644 index bc10df8..0000000 --- a/server/internal/library/jwt/jwt.go +++ /dev/null @@ -1,113 +0,0 @@ -// Package jwt -// @Link https://github.com/bufanyun/hotgo -// @Copyright Copyright (c) 2023 HotGo CLI -// @Author Ms <133814250@qq.com> -// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE -package jwt - -import ( - "context" - "fmt" - j "github.com/dgrijalva/jwt-go" - "github.com/gogf/gf/v2/crypto/gmd5" - "github.com/gogf/gf/v2/errors/gerror" - "github.com/gogf/gf/v2/frame/g" - "github.com/gogf/gf/v2/net/ghttp" - "github.com/gogf/gf/v2/text/gstr" - "github.com/gogf/gf/v2/util/gconv" - "hotgo/internal/consts" - "hotgo/internal/library/cache" - "hotgo/internal/model" - "time" -) - -// GenerateLoginToken 为指定用户生成token -func GenerateLoginToken(ctx context.Context, user *model.Identity, isRefresh bool) (string, error) { - var ( - jwtVersion = g.Cfg().MustGet(ctx, "jwt.version", "1.0") - jwtSign = g.Cfg().MustGet(ctx, "jwt.sign", "hotGo") - token = j.NewWithClaims(j.SigningMethodHS256, j.MapClaims{ - "id": user.Id, - "pid": user.Pid, - "deptId": user.DeptId, - "roleId": user.RoleId, - "roleKey": user.RoleKey, - "username": user.Username, - "realName": user.RealName, - "avatar": user.Avatar, - "email": user.Email, - "mobile": user.Mobile, - "exp": user.Exp, - "expires": user.Expires, - "app": user.App, - "isRefresh": isRefresh, - "jwtVersion": jwtVersion.String(), - }) - ) - - tokenString, err := token.SignedString(jwtSign.Bytes()) - if err != nil { - return "", err - } - - var ( - tokenStringMd5 = gmd5.MustEncryptString(tokenString) - // 绑定登录token - key = consts.CacheJwtToken + tokenStringMd5 - // 将有效期转为持续时间,单位:秒 - expires, _ = time.ParseDuration(fmt.Sprintf("+%vs", user.Expires)) - ) - - err = cache.Instance().Set(ctx, key, tokenString, expires) - if err != nil { - return "", err - } - - err = cache.Instance().Set(ctx, consts.CacheJwtUserBind+user.App+":"+gconv.String(user.Id), key, expires) - if err != nil { - return "", err - } - return tokenString, err -} - -// ParseToken 解析token -func ParseToken(tokenString string, secret []byte) (j.MapClaims, error) { - if tokenString == "" { - err := gerror.New("token 为空") - return nil, err - } - token, err := j.Parse(tokenString, func(token *j.Token) (interface{}, error) { - if _, ok := token.Method.(*j.SigningMethodHMAC); !ok { - return nil, gerror.Newf("unexpected signing method: %v", token.Header["alg"]) - } - return secret, nil - }) - - if token == nil { - err := gerror.New("token不存在") - return nil, err - } - - if claims, ok := token.Claims.(j.MapClaims); ok && token.Valid { - return claims, nil - } else { - return nil, err - } -} - -// GetAuthorization 获取authorization -func GetAuthorization(r *ghttp.Request) string { - // 默认从请求头获取 - var authorization = r.Header.Get("Authorization") - - // 如果请求头不存在则从get参数获取 - if authorization == "" { - return r.Get("authorization").String() - } - - return gstr.Replace(authorization, "Bearer ", "") -} - -func GenAuthKey(token string) string { - return gmd5.MustEncryptString(token) -} diff --git a/server/internal/library/location/location.go b/server/internal/library/location/location.go index 283a597..6f7fc43 100644 --- a/server/internal/library/location/location.go +++ b/server/internal/library/location/location.go @@ -8,8 +8,8 @@ package location import ( "context" "fmt" - "github.com/axgle/mahonia" "github.com/gogf/gf/v2/container/gmap" + "github.com/gogf/gf/v2/encoding/gcharset" "github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/net/ghttp" "github.com/gogf/gf/v2/text/gstr" @@ -67,19 +67,18 @@ func WhoisLocation(ctx context.Context, ip string) (*IpLocationData, error) { defer response.Close() - var ( - whoisData *WhoisRegionData - enc = mahonia.NewDecoder("gbk") - data = enc.ConvertString(response.ReadAllString()) - ) + str, err := gcharset.ToUTF8("GBK", response.ReadAllString()) + if err != nil { + return nil, err + } - if err = gconv.Struct(data, &whoisData); err != nil { + var whoisData *WhoisRegionData + if err = gconv.Struct([]byte(str), &whoisData); err != nil { return nil, err } return &IpLocationData{ - Ip: whoisData.Ip, - //Country string `json:"country"` + Ip: whoisData.Ip, Region: whoisData.Addr, Province: whoisData.Pro, ProvinceCode: gconv.Int64(whoisData.ProCode), diff --git a/server/internal/library/token/token.go b/server/internal/library/token/token.go new file mode 100644 index 0000000..e8e513f --- /dev/null +++ b/server/internal/library/token/token.go @@ -0,0 +1,299 @@ +package token + +import ( + "context" + "fmt" + "github.com/gogf/gf/v2/crypto/gmd5" + "github.com/gogf/gf/v2/errors/gerror" + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/net/ghttp" + "github.com/gogf/gf/v2/os/gtime" + "github.com/gogf/gf/v2/text/gstr" + "github.com/gogf/gf/v2/util/gconv" + "github.com/golang-jwt/jwt/v5" + "hotgo/internal/consts" + "hotgo/internal/library/cache" + "hotgo/internal/library/contexts" + "hotgo/internal/model" + "hotgo/utility/simple" + "time" +) + +type Claims struct { + *model.Identity + jwt.RegisteredClaims +} + +type Token struct { + ExpireAt int64 `json:"exp"` // token过期时间 + RefreshAt int64 `json:"ra"` // 刷新时间 + RefreshCount int64 `json:"rc"` // 刷新次数 +} + +var ( + config *model.TokenConfig + errorLogin = gerror.New("登录身份已失效,请重新登录!") + errorMultiLogin = gerror.New("账号存在异地登录,如非本人操作请及时修改登录密码!") +) + +func SetConfig(c *model.TokenConfig) { + config = c +} + +func GetConfig() *model.TokenConfig { + return config +} + +// Login 登录 +func Login(ctx context.Context, user *model.Identity) (string, int64, error) { + claims := Claims{ + user, + jwt.RegisteredClaims{}, + } + + header, err := jwt.NewWithClaims(jwt.SigningMethodHS256, claims).SignedString([]byte(config.SecretKey)) + if err != nil { + return "", 0, err + } + + var ( + now = gtime.Now() + // 认证key + authKey = GetAuthKey(header) + // 登录token + tokenKey = GetTokenKey(user.App, authKey) + // 身份绑定 + bindKey = GetBindKey(user.App, user.Id) + // 有效时长 + duration = time.Second * gconv.Duration(config.Expires) + ) + + token := &Token{ + ExpireAt: now.Unix() + config.Expires, + RefreshAt: now.Unix(), + RefreshCount: 0, + } + + if err = cache.Instance().Set(ctx, tokenKey, token, duration); err != nil { + return "", 0, err + } + + if err = cache.Instance().Set(ctx, bindKey, tokenKey, duration); err != nil { + return "", 0, err + } + + return header, config.Expires, nil +} + +// Logout 注销登录 +func Logout(r *ghttp.Request) (err error) { + var ( + ctx = r.Context() + header = GetAuthorization(r) + ) + + if header == "" { + err = errorLogin + return + } + + claims, err := parseToken(ctx, header) + if err != nil { + return + } + + var ( + // 认证key + authKey = GetAuthKey(header) + // 登录token + tokenKey = GetTokenKey(contexts.GetModule(ctx), authKey) + // 身份绑定 + bindKey = GetBindKey(contexts.GetModule(ctx), claims.Id) + ) + + // 删除token + if _, err = cache.Instance().Remove(ctx, tokenKey); err != nil { + return + } + + if !config.MultiLogin { + if _, err = cache.Instance().Remove(ctx, bindKey); err != nil { + return + } + } + return +} + +// ParseLoginUser 解析登录用户信息 +func ParseLoginUser(r *ghttp.Request) (user *model.Identity, err error) { + var ( + ctx = r.Context() + header = GetAuthorization(r) + ) + + if header == "" { + err = errorLogin + return + } + + claims, err := parseToken(ctx, header) + if err != nil { + return + } + + var ( + authKey = GetAuthKey(header) + // 登录token + tokenKey = GetTokenKey(claims.App, authKey) + // 身份绑定 + bindKey = GetBindKey(claims.App, claims.Id) + ) + + // 检查token是否存在 + tk, err := cache.Instance().Get(ctx, tokenKey) + if err != nil { + g.Log().Debugf(ctx, "get tokenKey err:%+v", err) + err = errorLogin + return + } + + if tk.IsEmpty() { + g.Log().Debug(ctx, "token isEmpty") + err = errorLogin + return + } + + var token *Token + if err = tk.Scan(&token); err != nil { + g.Log().Debugf(ctx, "token scan err:%+v", err) + err = errorLogin + return + } + + if token == nil { + g.Log().Debugf(ctx, "token = nil") + err = errorLogin + return + } + + now := gtime.Now() + if token.ExpireAt < now.Unix() { + g.Log().Debugf(ctx, "token expired.") + err = errorLogin + return + } + + // 是否允许多端登录 + if !config.MultiLogin { + origin, err := cache.Instance().Get(ctx, bindKey) + if err != nil { + g.Log().Debugf(ctx, "bindKey get err:%+v", err) + err = errorLogin + return nil, err + } + + if origin == nil || origin.IsEmpty() { + g.Log().Debug(ctx, "bindKey isEmpty") + err = errorLogin + return nil, err + } + + if tokenKey != origin.String() { + g.Log().Debugf(ctx, "bindKey offsite login tokenKey:%v, origin:%v", tokenKey, origin.String()) + err = errorMultiLogin + return nil, err + } + } + + // 自动刷新token有效期 + refreshToken := func() { + // 未开启自动刷新 + if !config.AutoRefresh { + return + } + + // 刷新次数已达上限 + if config.MaxRefreshTimes != -1 && token.RefreshCount >= config.MaxRefreshTimes { + return + } + + // 未达到刷新间隔 + if gtime.New(token.RefreshAt).Unix()+config.RefreshInterval > now.Unix() { + return + } + + // 刷新有效期 + token.ExpireAt = now.Unix() + config.Expires + token.RefreshAt = now.Unix() + token.RefreshCount += 1 + + duration := time.Second * gconv.Duration(config.Expires) + + if err = cache.Instance().Set(ctx, tokenKey, token, duration); err != nil { + return + } + + if err = cache.Instance().Set(ctx, bindKey, tokenKey, duration); err != nil { + return + } + } + + simple.SafeGo(ctx, func(ctx context.Context) { + refreshToken() + }) + + user = new(model.Identity) + user = claims.Identity + return +} + +// parseToken 解析jwt令牌 +func parseToken(ctx context.Context, header string) (*Claims, error) { + token, err := jwt.ParseWithClaims(header, &Claims{}, func(token *jwt.Token) (interface{}, error) { + return []byte(config.SecretKey), nil + }) + + if err != nil { + g.Log().Debugf(ctx, "parseToken err:%+v", err) + return nil, err + } + + if !token.Valid { + return nil, errorLogin + } + + claims, ok := token.Claims.(*Claims) + if !ok { + return nil, errorLogin + } + + return claims, nil +} + +// GetAuthorization 获取authorization +func GetAuthorization(r *ghttp.Request) string { + // 默认从请求头获取 + var authorization = r.Header.Get("Authorization") + + // 如果请求头不存在则从get参数获取 + if authorization == "" { + return r.Get("authorization").String() + } + + return gstr.Replace(authorization, "Bearer ", "") +} + +// GetAuthKey 认证key +func GetAuthKey(token string) string { + return gmd5.MustEncryptString(token) +} + +// GetTokenKey 令牌缓存key +func GetTokenKey(appName, authKey string) string { + return fmt.Sprintf("%v:%v:%v", consts.CacheToken, appName, authKey) +} + +// GetBindKey 令牌身份绑定key +func GetBindKey(appName string, userId int64) string { + return fmt.Sprintf("%v:%v:%v", consts.CacheTokenBind, appName, userId) +} diff --git a/server/internal/logic/admin/member.go b/server/internal/logic/admin/member.go index 819a130..087648a 100644 --- a/server/internal/logic/admin/member.go +++ b/server/internal/logic/admin/member.go @@ -21,7 +21,7 @@ import ( "hotgo/internal/library/contexts" "hotgo/internal/library/hgorm/handler" "hotgo/internal/library/hgorm/hook" - "hotgo/internal/library/jwt" + "hotgo/internal/library/token" "hotgo/internal/model" "hotgo/internal/model/do" "hotgo/internal/model/entity" @@ -663,9 +663,8 @@ func (s *sAdminMember) LoginMemberInfo(ctx context.Context) (res *adminin.LoginM // Login 提交登录 func (s *sAdminMember) Login(ctx context.Context, in adminin.MemberLoginInp) (res *adminin.MemberLoginModel, err error) { var ( - ro *entity.AdminRole - mb *entity.AdminMember - expires = g.Cfg().MustGet(ctx, "jwt.expires", 1).Int64() + ro *entity.AdminRole + mb *entity.AdminMember ) if err = dao.AdminMember.Ctx(ctx).Where("username", in.Username).Scan(&mb); err != nil { @@ -707,7 +706,7 @@ func (s *sAdminMember) Login(ctx context.Context, in adminin.MemberLoginInp) (re return } - identity := &model.Identity{ + user := &model.Identity{ Id: mb.Id, Pid: mb.Pid, DeptId: mb.DeptId, @@ -718,20 +717,17 @@ func (s *sAdminMember) Login(ctx context.Context, in adminin.MemberLoginInp) (re Avatar: mb.Avatar, Email: mb.Email, Mobile: mb.Mobile, - Exp: gtime.Timestamp() + expires, - Expires: expires, App: consts.AppAdmin, + LoginAt: gtime.Now(), } - token, err := jwt.GenerateLoginToken(ctx, identity, false) + loginToken, expires, err := token.Login(ctx, user) if err != nil { - err = gerror.New(err.Error()) - return + return nil, err } update := do.AdminMember{ - AuthKey: jwt.GenAuthKey(token), - LastActiveAt: gtime.Now(), + LastActiveAt: user.LoginAt, } // 更新登录信息 @@ -740,8 +736,8 @@ func (s *sAdminMember) Login(ctx context.Context, in adminin.MemberLoginInp) (re } res = &adminin.MemberLoginModel{ - Id: identity.Id, - Token: token, + Id: user.Id, + Token: loginToken, Expires: expires, } diff --git a/server/internal/logic/admin/role.go b/server/internal/logic/admin/role.go index ea53cdc..1f628db 100644 --- a/server/internal/logic/admin/role.go +++ b/server/internal/logic/admin/role.go @@ -21,7 +21,6 @@ import ( "hotgo/internal/model/input/adminin" "hotgo/internal/model/input/form" "hotgo/internal/service" - "hotgo/utility/auth" "hotgo/utility/convert" "hotgo/utility/tree" "sort" @@ -39,10 +38,6 @@ func init() { // Verify 验证权限 func (s *sAdminRole) Verify(ctx context.Context, path, method string) bool { - if auth.IsExceptAuth(ctx, path) { - return true - } - var ( user = contexts.Get(ctx).User superRoleKey = g.Cfg().MustGet(ctx, "hotgo.admin.superRoleKey") @@ -57,6 +52,7 @@ func (s *sAdminRole) Verify(ctx context.Context, path, method string) bool { if service.AdminMember().VerifySuperId(ctx, user.Id) || user.RoleKey == superRoleKey.String() { return true } + ok, err := casbin.Enforcer.Enforce(user.RoleKey, path, method) if err != nil { g.Log().Infof(ctx, "admin Verify Enforce err:%+v", err) diff --git a/server/internal/logic/common/wechat.go b/server/internal/logic/common/wechat.go index a8c9d8d..d9b6dfd 100644 --- a/server/internal/logic/common/wechat.go +++ b/server/internal/logic/common/wechat.go @@ -19,8 +19,8 @@ import ( "hotgo/internal/consts" "hotgo/internal/library/cache" "hotgo/internal/library/contexts" - "hotgo/internal/library/jwt" "hotgo/internal/library/response" + "hotgo/internal/library/token" "hotgo/internal/library/wechat" "hotgo/internal/model/input/commonin" "hotgo/internal/service" @@ -65,7 +65,7 @@ func (s *sCommonWechat) Authorize(ctx context.Context, in commonin.WechatAuthori path = gmeta.Get(common.WechatAuthorizeCallReq{}, "path").String() redirectUri = basic.Domain + prefix + path memberId = contexts.GetUserId(ctx) - state = s.GetCacheKey(in.Type, jwt.GenAuthKey(jwt.GetAuthorization(request))) + state = s.GetCacheKey(in.Type, token.GetAuthKey(token.GetAuthorization(request))) scope string ) @@ -109,14 +109,14 @@ func (s *sCommonWechat) AuthorizeCall(ctx context.Context, in commonin.WechatAut defer delete(s.temp, in.State) - token, err := wechat.GetUserAccessToken(ctx, in.Code) + tk, err := wechat.GetUserAccessToken(ctx, in.Code) if err != nil { return } switch data.Type { case consts.WechatAuthorizeOpenId: // 设置openid - cache.Instance().Set(ctx, data.State, token.OpenID, time.Hour*24*7) + cache.Instance().Set(ctx, data.State, tk.OpenID, time.Hour*24*7) case consts.WechatAuthorizeBindLogin: // 绑定微信登录 // ... default: @@ -131,7 +131,7 @@ func (s *sCommonWechat) AuthorizeCall(ctx context.Context, in commonin.WechatAut // GetOpenId 从缓存中获取临时openid func (s *sCommonWechat) GetOpenId(ctx context.Context) (openId string, err error) { request := ghttp.RequestFromCtx(ctx) - key := s.GetCacheKey(consts.WechatAuthorizeOpenId, jwt.GenAuthKey(jwt.GetAuthorization(request))) + key := s.GetCacheKey(consts.WechatAuthorizeOpenId, token.GetAuthKey(token.GetAuthorization(request))) date, err := cache.Instance().Get(ctx, key) if err != nil { err = gerror.Newf("GetOpenId err:%+v", err.Error()) diff --git a/server/internal/logic/middleware/admin_auth.go b/server/internal/logic/middleware/admin_auth.go index 4c5663a..38daacd 100644 --- a/server/internal/logic/middleware/admin_auth.go +++ b/server/internal/logic/middleware/admin_auth.go @@ -3,7 +3,6 @@ // @Copyright Copyright (c) 2023 HotGo CLI // @Author Ms <133814250@qq.com> // @License https://github.com/bufanyun/hotgo/blob/master/LICENSE -// package middleware import ( @@ -15,27 +14,35 @@ import ( "hotgo/internal/library/contexts" "hotgo/internal/library/response" "hotgo/internal/service" - "hotgo/utility/auth" ) // AdminAuth 后台鉴权中间件 func (s *sMiddleware) AdminAuth(r *ghttp.Request) { - var ctx = r.Context() - // 替换掉模块前缀 - routerPrefix := g.Cfg().MustGet(ctx, "router.admin.prefix", "/admin") - path := gstr.Replace(r.URL.Path, routerPrefix.String(), "", 1) + var ( + ctx = r.Context() + prefix = g.Cfg().MustGet(ctx, "router.admin.prefix", "/admin").String() + path = gstr.Replace(r.URL.Path, prefix, "", 1) + ) - /// 不需要验证登录的路由地址 - if auth.IsExceptLogin(ctx, path) { + // 不需要验证登录的路由地址 + if isExceptLogin(ctx, consts.AppAdmin, path) { r.Middleware.Next() return } - if err := inspectAuth(r, consts.AppAdmin); err != nil { + // 将用户信息传递到上下文中 + if err := deliverUserContext(r); err != nil { + g.Log().Warningf(ctx, "deliverUserContext err:%+v", err) response.JsonExit(r, gcode.CodeNotAuthorized.Code(), err.Error()) return } + // 不需要验证权限的路由地址 + if isExceptAuth(ctx, consts.AppAdmin, path) { + r.Middleware.Next() + return + } + // 验证路由访问权限 if !service.AdminRole().Verify(ctx, path, r.Method) { g.Log().Debugf(ctx, "AdminAuth fail path:%+v, GetRoleKey:%+v, r.Method:%+v", path, contexts.GetRoleKey(ctx), r.Method) diff --git a/server/internal/logic/middleware/api_auth.go b/server/internal/logic/middleware/api_auth.go index 4a9a2e0..0e9655a 100644 --- a/server/internal/logic/middleware/api_auth.go +++ b/server/internal/logic/middleware/api_auth.go @@ -3,7 +3,6 @@ // @Copyright Copyright (c) 2023 HotGo CLI // @Author Ms <133814250@qq.com> // @License https://github.com/bufanyun/hotgo/blob/master/LICENSE -// package middleware import ( @@ -13,34 +12,30 @@ import ( "github.com/gogf/gf/v2/text/gstr" "hotgo/internal/consts" "hotgo/internal/library/response" - "hotgo/utility/auth" ) // ApiAuth API鉴权中间件 func (s *sMiddleware) ApiAuth(r *ghttp.Request) { - var ctx = r.Context() + var ( + ctx = r.Context() + prefix = g.Cfg().MustGet(ctx, "router.api.prefix", "/api").String() + path = gstr.Replace(r.URL.Path, prefix, "", 1) + ) - // 替换掉模块前缀 - routerPrefix := g.Cfg().MustGet(ctx, "router.api.prefix", "/api") - path := gstr.Replace(r.URL.Path, routerPrefix.String(), "", 1) - - /// 不需要验证登录的路由地址 - if auth.IsExceptLogin(ctx, path) { + // 不需要验证登录的路由地址 + if isExceptLogin(ctx, consts.AppApi, path) { r.Middleware.Next() return } - if err := inspectAuth(r, consts.AppAdmin); err != nil { + // 将用户信息传递到上下文中 + if err := deliverUserContext(r); err != nil { response.JsonExit(r, gcode.CodeNotAuthorized.Code(), err.Error()) return } - //// 验证路由访问权限 - //verify := service.AdminRole().Verify(ctx, path, r.Method) - //if !verify { - // response.JsonExit(r, consts.CodeSecurityReason, "你没有访问权限!") - // return - //} + // 验证路由访问权限 + // ... r.Middleware.Next() } diff --git a/server/internal/logic/middleware/init.go b/server/internal/logic/middleware/init.go index ac0ba75..331e0d8 100644 --- a/server/internal/logic/middleware/init.go +++ b/server/internal/logic/middleware/init.go @@ -6,22 +6,21 @@ package middleware import ( - "github.com/gogf/gf/v2/crypto/gmd5" + "context" + "fmt" "github.com/gogf/gf/v2/encoding/gjson" "github.com/gogf/gf/v2/errors/gcode" - "github.com/gogf/gf/v2/errors/gerror" "github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/net/ghttp" "github.com/gogf/gf/v2/text/gstr" - "github.com/gogf/gf/v2/util/gconv" "hotgo/internal/consts" "hotgo/internal/library/addons" - "hotgo/internal/library/cache" "hotgo/internal/library/contexts" - "hotgo/internal/library/jwt" "hotgo/internal/library/response" + "hotgo/internal/library/token" "hotgo/internal/model" "hotgo/internal/service" + "hotgo/utility/validate" "net/http" "strings" ) @@ -33,10 +32,10 @@ type sMiddleware struct { } func init() { - service.RegisterMiddleware(New()) + service.RegisterMiddleware(NewMiddleware()) } -func New() *sMiddleware { +func NewMiddleware() *sMiddleware { return &sMiddleware{ LoginUrl: "/common", DemoWhiteList: g.Map{ @@ -124,85 +123,38 @@ func (s *sMiddleware) Addon(r *ghttp.Request) { r.Middleware.Next() } -// inspectAuth 检查并完成身份认证 -func inspectAuth(r *ghttp.Request, appName string) (err error) { - var ( - ctx = r.Context() - user = new(model.Identity) - authorization = jwt.GetAuthorization(r) - customCtx = &model.Context{} - ) - - if authorization == "" { - err = gerror.New("请先登录!") +// deliverUserContext 将用户信息传递到上下文中 +func deliverUserContext(r *ghttp.Request) (err error) { + user, err := token.ParseLoginUser(r) + if err != nil { return } - - // 获取jwtToken - jwtToken := consts.CacheJwtToken + gmd5.MustEncryptString(authorization) - jwtSign := g.Cfg().MustGet(ctx, "jwt.sign", "hotgo") - - data, parseErr := jwt.ParseToken(authorization, jwtSign.Bytes()) - if parseErr != nil { - err = gerror.Newf("token不正确或已过期! err :%+v", parseErr.Error()) - return - } - - if parseErr = gconv.Struct(data, &user); parseErr != nil { - err = gerror.Newf("登录信息解析异常,请重新登录! err :%+v", parseErr.Error()) - return - } - - // 判断token跟redis的缓存的token是否一样 - isContains, containsErr := cache.Instance().Contains(ctx, jwtToken) - if containsErr != nil { - err = gerror.Newf("token无效! err :%+v", containsErr.Error()) - return - } - - if !isContains { - err = gerror.Newf("token已过期") - return - } - - // 是否开启多端登录 - if !g.Cfg().MustGet(ctx, "jwt.multiPort", true).Bool() { - key := consts.CacheJwtUserBind + appName + ":" + gconv.String(user.Id) - originJwtToken, originErr := cache.Instance().Get(ctx, key) - if originErr != nil { - err = gerror.Newf("信息异常,请重新登录! err :%+v", originErr.Error()) - return - } - - if originJwtToken == nil || originJwtToken.IsEmpty() { - err = gerror.New("token已过期!") - return - } - - if jwtToken != originJwtToken.String() { - err = gerror.New("账号已在其他地方登录!") - return - } - } - - // 保存到上下文 - if user != nil { - customCtx.User = &model.Identity{ - Id: user.Id, - Pid: user.Pid, - DeptId: user.DeptId, - RoleId: user.RoleId, - RoleKey: user.RoleKey, - Username: user.Username, - RealName: user.RealName, - Avatar: user.Avatar, - Email: user.Email, - Mobile: user.Mobile, - Exp: user.Exp, - Expires: user.Expires, - App: user.App, - } - } - contexts.SetUser(ctx, customCtx.User) + contexts.SetUser(r.Context(), user) return } + +// isExceptAuth 是否是不需要验证权限的路由地址 +func isExceptAuth(ctx context.Context, appName, path string) bool { + pathList := g.Cfg().MustGet(ctx, fmt.Sprintf("router.%v.exceptAuth", appName)).Strings() + + for i := 0; i < len(pathList); i++ { + if validate.InSliceExistStr(pathList[i], path) { + return true + } + } + + return false +} + +// isExceptLogin 是否是不需要登录的路由地址 +func isExceptLogin(ctx context.Context, appName, path string) bool { + pathList := g.Cfg().MustGet(ctx, fmt.Sprintf("router.%v.exceptLogin", appName)).Strings() + + for i := 0; i < len(pathList); i++ { + if validate.InSliceExistStr(pathList[i], path) { + return true + } + } + + return false +} diff --git a/server/internal/logic/middleware/weboscket.go b/server/internal/logic/middleware/weboscket_auth.go similarity index 56% rename from server/internal/logic/middleware/weboscket.go rename to server/internal/logic/middleware/weboscket_auth.go index bf4374d..3c1f3ad 100644 --- a/server/internal/logic/middleware/weboscket.go +++ b/server/internal/logic/middleware/weboscket_auth.go @@ -3,7 +3,6 @@ // @Copyright Copyright (c) 2023 HotGo CLI // @Author Ms <133814250@qq.com> // @License https://github.com/bufanyun/hotgo/blob/master/LICENSE -// package middleware import ( @@ -13,24 +12,24 @@ import ( "github.com/gogf/gf/v2/text/gstr" "hotgo/internal/consts" "hotgo/internal/library/response" - "hotgo/utility/auth" ) -// WebSocketToken 检查ws连接token -func (s *sMiddleware) WebSocketToken(r *ghttp.Request) { - var ctx = r.Context() +// WebSocketAuth websocket鉴权中间件 +func (s *sMiddleware) WebSocketAuth(r *ghttp.Request) { + var ( + ctx = r.Context() + prefix = g.Cfg().MustGet(ctx, "router.websocket.prefix", "/websocket").String() + path = gstr.Replace(r.URL.Path, prefix, "", 1) + ) - // 替换掉模块前缀 - routerPrefix := g.Cfg().MustGet(ctx, "router.ws.prefix", "/socket") - path := gstr.Replace(r.URL.Path, routerPrefix.String(), "", 1) - - /// 不需要验证登录的路由地址 - if auth.IsExceptLogin(ctx, path) { + // 不需要验证登录的路由地址 + if isExceptLogin(ctx, consts.AppWebSocket, path) { r.Middleware.Next() return } - if err := inspectAuth(r, consts.AppAdmin); err != nil { + // 将用户信息传递到上下文中 + if err := deliverUserContext(r); err != nil { response.JsonExit(r, gcode.CodeNotAuthorized.Code(), err.Error()) return } diff --git a/server/internal/logic/sys/config.go b/server/internal/logic/sys/config.go index 7870125..7c67eb8 100644 --- a/server/internal/logic/sys/config.go +++ b/server/internal/logic/sys/config.go @@ -17,6 +17,7 @@ import ( "hotgo/internal/consts" "hotgo/internal/dao" "hotgo/internal/library/payment" + "hotgo/internal/library/token" "hotgo/internal/library/wechat" "hotgo/internal/model" "hotgo/internal/model/entity" @@ -74,6 +75,12 @@ func (s *sSysConfig) InitConfig(ctx context.Context) { } payment.SetConfig(pay) + tk, err := s.GetLoadToken(ctx) + if err != nil { + g.Log().Fatalf(ctx, "init token conifg fail:%+v", err) + } + token.SetConfig(tk) + } // GetLoadTCP 获取本地tcp配置 @@ -94,6 +101,12 @@ func (s *sSysConfig) GetLoadGenerate(ctx context.Context) (conf *model.GenerateC return } +// GetLoadToken 获取本地token配置 +func (s *sSysConfig) GetLoadToken(ctx context.Context) (conf *model.TokenConfig, err error) { + err = g.Cfg().MustGet(ctx, "token").Scan(&conf) + return +} + // GetWechat 获取微信配置 func (s *sSysConfig) GetWechat(ctx context.Context) (conf *model.WechatConfig, err error) { models, err := s.GetConfigByGroup(ctx, sysin.GetConfigInp{Group: "wechat"}) diff --git a/server/internal/model/config.go b/server/internal/model/config.go index 397f613..935bd1c 100644 --- a/server/internal/model/config.go +++ b/server/internal/model/config.go @@ -261,7 +261,18 @@ type TCPClientConnConfig struct { SecretKey string `json:"secretKey"` } +// TCPConfig tcp服务器配置 type TCPConfig struct { Server *TCPServerConfig `json:"server"` Client *TCPClientConfig `json:"client"` } + +// TokenConfig 登录令牌配置 +type TokenConfig struct { + SecretKey string `json:"secretKey"` + Expires int64 `json:"expires"` + AutoRefresh bool `json:"autoRefresh"` + RefreshInterval int64 `json:"refreshInterval"` + MaxRefreshTimes int64 `json:"maxRefreshTimes"` + MultiLogin bool `json:"multiLogin"` +} diff --git a/server/internal/model/context.go b/server/internal/model/context.go index 3579b76..1568077 100644 --- a/server/internal/model/context.go +++ b/server/internal/model/context.go @@ -7,6 +7,7 @@ package model import ( "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/os/gtime" ) // Context 请求上下文结构 @@ -20,17 +21,16 @@ type Context struct { // Identity 通用身份模型 type Identity struct { - Id int64 `json:"id" description:"用户ID"` - Pid int64 `json:"pid" description:"上级ID"` - DeptId int64 `json:"deptId" description:"部门ID"` - RoleId int64 `json:"roleId" description:"角色ID"` - RoleKey string `json:"roleKey" description:"角色唯一标识符"` - Username string `json:"username" description:"用户名"` - RealName string `json:"realName" description:"姓名"` - Avatar string `json:"avatar" description:"头像"` - Email string `json:"email" description:"邮箱"` - Mobile string `json:"mobile" description:"手机号码"` - Exp int64 `json:"exp" description:"登录有效期截止时间戳"` - Expires int64 `json:"expires" description:"登录有效期"` - App string `json:"app" description:"登录应用"` + Id int64 `json:"id" description:"用户ID"` + Pid int64 `json:"pid" description:"上级ID"` + DeptId int64 `json:"deptId" description:"部门ID"` + RoleId int64 `json:"roleId" description:"角色ID"` + RoleKey string `json:"roleKey" description:"角色唯一标识符"` + Username string `json:"username" description:"用户名"` + RealName string `json:"realName" description:"姓名"` + Avatar string `json:"avatar" description:"头像"` + Email string `json:"email" description:"邮箱"` + Mobile string `json:"mobile" description:"手机号码"` + App string `json:"app" description:"登录应用"` + LoginAt *gtime.Time `json:"loginAt" description:"登录时间"` } diff --git a/server/internal/model/do/admin_member.go b/server/internal/model/do/admin_member.go index b5427c6..5b57c6c 100644 --- a/server/internal/model/do/admin_member.go +++ b/server/internal/model/do/admin_member.go @@ -20,7 +20,6 @@ type AdminMember struct { Username interface{} // 帐号 PasswordHash interface{} // 密码 Salt interface{} // 密码盐 - AuthKey interface{} // 授权令牌 PasswordResetToken interface{} // 密码重置令牌 Integral interface{} // 积分 Balance interface{} // 余额 diff --git a/server/internal/model/do/sys_log.go b/server/internal/model/do/sys_log.go index 131160b..7212288 100644 --- a/server/internal/model/do/sys_log.go +++ b/server/internal/model/do/sys_log.go @@ -28,7 +28,7 @@ type SysLog struct { ProvinceId interface{} // 省编码 CityId interface{} // 市编码 ErrorCode interface{} // 报错code - ErrorMsg interface{} // 报错信息 + ErrorMsg interface{} // 对外错误提示 ErrorData *gjson.Json // 报错日志 UserAgent interface{} // UA信息 TakeUpTime interface{} // 请求耗时 diff --git a/server/internal/model/entity/admin_member.go b/server/internal/model/entity/admin_member.go index 53012f2..339c0e1 100644 --- a/server/internal/model/entity/admin_member.go +++ b/server/internal/model/entity/admin_member.go @@ -18,7 +18,6 @@ type AdminMember struct { Username string `json:"username" description:"帐号"` PasswordHash string `json:"passwordHash" description:"密码"` Salt string `json:"salt" description:"密码盐"` - AuthKey string `json:"authKey" description:"授权令牌"` PasswordResetToken string `json:"passwordResetToken" description:"密码重置令牌"` Integral float64 `json:"integral" description:"积分"` Balance float64 `json:"balance" description:"余额"` diff --git a/server/internal/model/entity/sys_log.go b/server/internal/model/entity/sys_log.go index fa10da6..0c00078 100644 --- a/server/internal/model/entity/sys_log.go +++ b/server/internal/model/entity/sys_log.go @@ -26,7 +26,7 @@ type SysLog struct { ProvinceId int64 `json:"provinceId" description:"省编码"` CityId int64 `json:"cityId" description:"市编码"` ErrorCode int `json:"errorCode" description:"报错code"` - ErrorMsg string `json:"errorMsg" description:"报错信息"` + ErrorMsg string `json:"errorMsg" description:"对外错误提示"` ErrorData *gjson.Json `json:"errorData" description:"报错日志"` UserAgent string `json:"userAgent" description:"UA信息"` TakeUpTime int64 `json:"takeUpTime" description:"请求耗时"` diff --git a/server/internal/router/websocket.go b/server/internal/router/websocket.go index db29f3b..3d03905 100644 --- a/server/internal/router/websocket.go +++ b/server/internal/router/websocket.go @@ -3,7 +3,6 @@ // @Copyright Copyright (c) 2023 HotGo CLI // @Author Ms <133814250@qq.com> // @License https://github.com/bufanyun/hotgo/blob/master/LICENSE -// package router import ( @@ -27,7 +26,7 @@ func WebSocket(ctx context.Context, group *ghttp.RouterGroup) { ) // ws连接中间件 - group.Middleware(service.Middleware().WebSocketToken) + group.Middleware(service.Middleware().WebSocketAuth) // ws group.GET("/", websocket.WsPage) diff --git a/server/internal/service/admin.go b/server/internal/service/admin.go index 3124e2c..42f97a8 100644 --- a/server/internal/service/admin.go +++ b/server/internal/service/admin.go @@ -19,6 +19,86 @@ import ( ) type ( + IAdminRole interface { + Verify(ctx context.Context, path, method string) bool + List(ctx context.Context, in adminin.RoleListInp) (res *adminin.RoleListModel, totalCount int, err error) + GetName(ctx context.Context, RoleId int64) (name string, err error) + GetMemberList(ctx context.Context, RoleId int64) (list []*adminin.RoleListModel, err error) + GetPermissions(ctx context.Context, reqInfo *role.GetPermissionsReq) (MenuIds []int64, err error) + UpdatePermissions(ctx context.Context, reqInfo *role.UpdatePermissionsReq) (err error) + Edit(ctx context.Context, in *role.EditReq) (err error) + Delete(ctx context.Context, in *role.DeleteReq) (err error) + DataScopeSelect(ctx context.Context) (res form.Selects) + DataScopeEdit(ctx context.Context, in *adminin.DataScopeEditInp) (err error) + } + IAdminNotice interface { + Model(ctx context.Context, option ...*handler.Option) *gdb.Model + Delete(ctx context.Context, in adminin.NoticeDeleteInp) (err error) + Edit(ctx context.Context, in adminin.NoticeEditInp) (err error) + Status(ctx context.Context, in adminin.NoticeStatusInp) (err error) + MaxSort(ctx context.Context, in adminin.NoticeMaxSortInp) (res *adminin.NoticeMaxSortModel, err error) + View(ctx context.Context, in adminin.NoticeViewInp) (res *adminin.NoticeViewModel, err error) + List(ctx context.Context, in adminin.NoticeListInp) (list []*adminin.NoticeListModel, totalCount int, err error) + PullMessages(ctx context.Context, in adminin.PullMessagesInp) (res *adminin.PullMessagesModel, err error) + UnreadCount(ctx context.Context, in adminin.NoticeUnreadCountInp) (res *adminin.NoticeUnreadCountModel, err error) + UpRead(ctx context.Context, in adminin.NoticeUpReadInp) (err error) + ReadAll(ctx context.Context, in adminin.NoticeReadAllInp) (err error) + MessageList(ctx context.Context, in adminin.NoticeMessageListInp) (list []*adminin.NoticeMessageListModel, totalCount int, err error) + } + IAdminCash interface { + View(ctx context.Context, in adminin.CashViewInp) (res *adminin.CashViewModel, err error) + List(ctx context.Context, in adminin.CashListInp) (list []*adminin.CashListModel, totalCount int, err error) + Apply(ctx context.Context, in adminin.CashApplyInp) (err error) + Payment(ctx context.Context, in adminin.CashPaymentInp) (err error) + } + IAdminCreditsLog interface { + Model(ctx context.Context, option ...*handler.Option) *gdb.Model + SaveBalance(ctx context.Context, in adminin.CreditsLogSaveBalanceInp) (res *adminin.CreditsLogSaveBalanceModel, err error) + SaveIntegral(ctx context.Context, in adminin.CreditsLogSaveIntegralInp) (res *adminin.CreditsLogSaveIntegralModel, err error) + List(ctx context.Context, in adminin.CreditsLogListInp) (list []*adminin.CreditsLogListModel, totalCount int, err error) + Export(ctx context.Context, in adminin.CreditsLogListInp) (err error) + } + IAdminDept interface { + NameUnique(ctx context.Context, in adminin.DeptNameUniqueInp) (res *adminin.DeptNameUniqueModel, err error) + Delete(ctx context.Context, in adminin.DeptDeleteInp) (err error) + Edit(ctx context.Context, in adminin.DeptEditInp) (err error) + Status(ctx context.Context, in adminin.DeptStatusInp) (err error) + MaxSort(ctx context.Context, in adminin.DeptMaxSortInp) (res *adminin.DeptMaxSortModel, err error) + View(ctx context.Context, in adminin.DeptViewInp) (res *adminin.DeptViewModel, err error) + Option(ctx context.Context, in adminin.DeptOptionInp) (res *adminin.DeptOptionModel, totalCount int, err error) + List(ctx context.Context, in adminin.DeptListInp) (res *adminin.DeptListModel, err error) + GetName(ctx context.Context, id int64) (name string, err error) + } + IAdminMember interface { + AddBalance(ctx context.Context, in adminin.MemberAddBalanceInp) (err error) + AddIntegral(ctx context.Context, in adminin.MemberAddIntegralInp) (err error) + UpdateCash(ctx context.Context, in adminin.MemberUpdateCashInp) (err error) + UpdateEmail(ctx context.Context, in adminin.MemberUpdateEmailInp) (err error) + UpdateMobile(ctx context.Context, in adminin.MemberUpdateMobileInp) (err error) + UpdateProfile(ctx context.Context, in adminin.MemberUpdateProfileInp) (err error) + UpdatePwd(ctx context.Context, in adminin.MemberUpdatePwdInp) (err error) + ResetPwd(ctx context.Context, in adminin.MemberResetPwdInp) (err error) + EmailUnique(ctx context.Context, in adminin.MemberEmailUniqueInp) (res *adminin.MemberEmailUniqueModel, err error) + MobileUnique(ctx context.Context, in adminin.MemberMobileUniqueInp) (res *adminin.MemberMobileUniqueModel, err error) + NameUnique(ctx context.Context, in adminin.MemberNameUniqueInp) (res *adminin.MemberNameUniqueModel, err error) + VerifySuperId(ctx context.Context, verifyId int64) bool + Delete(ctx context.Context, in adminin.MemberDeleteInp) (err error) + Edit(ctx context.Context, in adminin.MemberEditInp) (err error) + View(ctx context.Context, in adminin.MemberViewInp) (res *adminin.MemberViewModel, err error) + List(ctx context.Context, in adminin.MemberListInp) (list []*adminin.MemberListModel, totalCount int, err error) + LoginMemberInfo(ctx context.Context) (res *adminin.LoginMemberInfoModel, err error) + Login(ctx context.Context, in adminin.MemberLoginInp) (res *adminin.MemberLoginModel, err error) + RoleMemberList(ctx context.Context, in adminin.RoleMemberListInp) (list []*adminin.MemberListModel, totalCount int, err error) + Status(ctx context.Context, in adminin.MemberStatusInp) (err error) + GetIdByCode(ctx context.Context, in adminin.GetIdByCodeInp) (res *adminin.GetIdByCodeModel, err error) + Select(ctx context.Context, in adminin.MemberSelectInp) (res []*adminin.MemberSelectModel, err error) + FilterAuthModel(ctx context.Context, memberId int64) *gdb.Model + MemberLoginStat(ctx context.Context, in adminin.MemberLoginStatInp) (res *adminin.MemberLoginStatModel, err error) + } + IAdminMemberPost interface { + UpdatePostIds(ctx context.Context, memberId int64, postIds []int64) (err error) + GetMemberByIds(ctx context.Context, memberId int64) (postIds []int64, err error) + } IAdminMenu interface { MaxSort(ctx context.Context, req *menu.MaxSortReq) (res *menu.MaxSortRes, err error) NameUnique(ctx context.Context, req *menu.NameUniqueReq) (res *menu.NameUniqueRes, err error) @@ -47,69 +127,6 @@ type ( View(ctx context.Context, in adminin.OrderViewInp) (res *adminin.OrderViewModel, err error) Status(ctx context.Context, in adminin.OrderStatusInp) (err error) } - IAdminRole interface { - Verify(ctx context.Context, path, method string) bool - List(ctx context.Context, in adminin.RoleListInp) (res *adminin.RoleListModel, totalCount int, err error) - GetName(ctx context.Context, RoleId int64) (name string, err error) - GetMemberList(ctx context.Context, RoleId int64) (list []*adminin.RoleListModel, err error) - GetPermissions(ctx context.Context, reqInfo *role.GetPermissionsReq) (MenuIds []int64, err error) - UpdatePermissions(ctx context.Context, reqInfo *role.UpdatePermissionsReq) (err error) - Edit(ctx context.Context, in *role.EditReq) (err error) - Delete(ctx context.Context, in *role.DeleteReq) (err error) - DataScopeSelect(ctx context.Context) (res form.Selects) - DataScopeEdit(ctx context.Context, in *adminin.DataScopeEditInp) (err error) - } - IAdminCreditsLog interface { - Model(ctx context.Context, option ...*handler.Option) *gdb.Model - SaveBalance(ctx context.Context, in adminin.CreditsLogSaveBalanceInp) (res *adminin.CreditsLogSaveBalanceModel, err error) - SaveIntegral(ctx context.Context, in adminin.CreditsLogSaveIntegralInp) (res *adminin.CreditsLogSaveIntegralModel, err error) - List(ctx context.Context, in adminin.CreditsLogListInp) (list []*adminin.CreditsLogListModel, totalCount int, err error) - Export(ctx context.Context, in adminin.CreditsLogListInp) (err error) - } - IAdminMemberPost interface { - UpdatePostIds(ctx context.Context, memberId int64, postIds []int64) (err error) - GetMemberByIds(ctx context.Context, memberId int64) (postIds []int64, err error) - } - IAdminMember interface { - AddBalance(ctx context.Context, in adminin.MemberAddBalanceInp) (err error) - AddIntegral(ctx context.Context, in adminin.MemberAddIntegralInp) (err error) - UpdateCash(ctx context.Context, in adminin.MemberUpdateCashInp) (err error) - UpdateEmail(ctx context.Context, in adminin.MemberUpdateEmailInp) (err error) - UpdateMobile(ctx context.Context, in adminin.MemberUpdateMobileInp) (err error) - UpdateProfile(ctx context.Context, in adminin.MemberUpdateProfileInp) (err error) - UpdatePwd(ctx context.Context, in adminin.MemberUpdatePwdInp) (err error) - ResetPwd(ctx context.Context, in adminin.MemberResetPwdInp) (err error) - EmailUnique(ctx context.Context, in adminin.MemberEmailUniqueInp) (res *adminin.MemberEmailUniqueModel, err error) - MobileUnique(ctx context.Context, in adminin.MemberMobileUniqueInp) (res *adminin.MemberMobileUniqueModel, err error) - NameUnique(ctx context.Context, in adminin.MemberNameUniqueInp) (res *adminin.MemberNameUniqueModel, err error) - VerifySuperId(ctx context.Context, verifyId int64) bool - Delete(ctx context.Context, in adminin.MemberDeleteInp) (err error) - Edit(ctx context.Context, in adminin.MemberEditInp) (err error) - View(ctx context.Context, in adminin.MemberViewInp) (res *adminin.MemberViewModel, err error) - List(ctx context.Context, in adminin.MemberListInp) (list []*adminin.MemberListModel, totalCount int, err error) - LoginMemberInfo(ctx context.Context) (res *adminin.LoginMemberInfoModel, err error) - Login(ctx context.Context, in adminin.MemberLoginInp) (res *adminin.MemberLoginModel, err error) - RoleMemberList(ctx context.Context, in adminin.RoleMemberListInp) (list []*adminin.MemberListModel, totalCount int, err error) - Status(ctx context.Context, in adminin.MemberStatusInp) (err error) - GetIdByCode(ctx context.Context, in adminin.GetIdByCodeInp) (res *adminin.GetIdByCodeModel, err error) - Select(ctx context.Context, in adminin.MemberSelectInp) (res []*adminin.MemberSelectModel, err error) - FilterAuthModel(ctx context.Context, memberId int64) *gdb.Model - MemberLoginStat(ctx context.Context, in adminin.MemberLoginStatInp) (res *adminin.MemberLoginStatModel, err error) - } - IAdminNotice interface { - Model(ctx context.Context, option ...*handler.Option) *gdb.Model - Delete(ctx context.Context, in adminin.NoticeDeleteInp) (err error) - Edit(ctx context.Context, in adminin.NoticeEditInp) (err error) - Status(ctx context.Context, in adminin.NoticeStatusInp) (err error) - MaxSort(ctx context.Context, in adminin.NoticeMaxSortInp) (res *adminin.NoticeMaxSortModel, err error) - View(ctx context.Context, in adminin.NoticeViewInp) (res *adminin.NoticeViewModel, err error) - List(ctx context.Context, in adminin.NoticeListInp) (list []*adminin.NoticeListModel, totalCount int, err error) - PullMessages(ctx context.Context, in adminin.PullMessagesInp) (res *adminin.PullMessagesModel, err error) - UnreadCount(ctx context.Context, in adminin.NoticeUnreadCountInp) (res *adminin.NoticeUnreadCountModel, err error) - UpRead(ctx context.Context, in adminin.NoticeUpReadInp) (err error) - ReadAll(ctx context.Context, in adminin.NoticeReadAllInp) (err error) - MessageList(ctx context.Context, in adminin.NoticeMessageListInp) (list []*adminin.NoticeMessageListModel, totalCount int, err error) - } IAdminPost interface { Delete(ctx context.Context, in adminin.PostDeleteInp) (err error) Edit(ctx context.Context, in adminin.PostEditInp) (err error) @@ -121,61 +138,22 @@ type ( GetMemberByStartName(ctx context.Context, memberId int64) (name string, err error) Status(ctx context.Context, in adminin.PostStatusInp) (err error) } - IAdminCash interface { - View(ctx context.Context, in adminin.CashViewInp) (res *adminin.CashViewModel, err error) - List(ctx context.Context, in adminin.CashListInp) (list []*adminin.CashListModel, totalCount int, err error) - Apply(ctx context.Context, in adminin.CashApplyInp) (err error) - Payment(ctx context.Context, in adminin.CashPaymentInp) (err error) - } - IAdminDept interface { - NameUnique(ctx context.Context, in adminin.DeptNameUniqueInp) (res *adminin.DeptNameUniqueModel, err error) - Delete(ctx context.Context, in adminin.DeptDeleteInp) (err error) - Edit(ctx context.Context, in adminin.DeptEditInp) (err error) - Status(ctx context.Context, in adminin.DeptStatusInp) (err error) - MaxSort(ctx context.Context, in adminin.DeptMaxSortInp) (res *adminin.DeptMaxSortModel, err error) - View(ctx context.Context, in adminin.DeptViewInp) (res *adminin.DeptViewModel, err error) - Option(ctx context.Context, in adminin.DeptOptionInp) (res *adminin.DeptOptionModel, totalCount int, err error) - List(ctx context.Context, in adminin.DeptListInp) (res *adminin.DeptListModel, err error) - GetName(ctx context.Context, id int64) (name string, err error) - } ) var ( localAdminCash IAdminCash + localAdminCreditsLog IAdminCreditsLog localAdminDept IAdminDept localAdminMember IAdminMember - localAdminNotice IAdminNotice - localAdminPost IAdminPost - localAdminRole IAdminRole - localAdminCreditsLog IAdminCreditsLog localAdminMemberPost IAdminMemberPost localAdminMenu IAdminMenu localAdminMonitor IAdminMonitor + localAdminNotice IAdminNotice localAdminOrder IAdminOrder + localAdminPost IAdminPost + localAdminRole IAdminRole ) -func AdminCreditsLog() IAdminCreditsLog { - if localAdminCreditsLog == nil { - panic("implement not found for interface IAdminCreditsLog, forgot register?") - } - return localAdminCreditsLog -} - -func RegisterAdminCreditsLog(i IAdminCreditsLog) { - localAdminCreditsLog = i -} - -func AdminMemberPost() IAdminMemberPost { - if localAdminMemberPost == nil { - panic("implement not found for interface IAdminMemberPost, forgot register?") - } - return localAdminMemberPost -} - -func RegisterAdminMemberPost(i IAdminMemberPost) { - localAdminMemberPost = i -} - func AdminMenu() IAdminMenu { if localAdminMenu == nil { panic("implement not found for interface IAdminMenu, forgot register?") @@ -198,26 +176,15 @@ func RegisterAdminMonitor(i IAdminMonitor) { localAdminMonitor = i } -func AdminOrder() IAdminOrder { - if localAdminOrder == nil { - panic("implement not found for interface IAdminOrder, forgot register?") +func AdminNotice() IAdminNotice { + if localAdminNotice == nil { + panic("implement not found for interface IAdminNotice, forgot register?") } - return localAdminOrder + return localAdminNotice } -func RegisterAdminOrder(i IAdminOrder) { - localAdminOrder = i -} - -func AdminRole() IAdminRole { - if localAdminRole == nil { - panic("implement not found for interface IAdminRole, forgot register?") - } - return localAdminRole -} - -func RegisterAdminRole(i IAdminRole) { - localAdminRole = i +func RegisterAdminNotice(i IAdminNotice) { + localAdminNotice = i } func AdminCash() IAdminCash { @@ -231,6 +198,17 @@ func RegisterAdminCash(i IAdminCash) { localAdminCash = i } +func AdminCreditsLog() IAdminCreditsLog { + if localAdminCreditsLog == nil { + panic("implement not found for interface IAdminCreditsLog, forgot register?") + } + return localAdminCreditsLog +} + +func RegisterAdminCreditsLog(i IAdminCreditsLog) { + localAdminCreditsLog = i +} + func AdminDept() IAdminDept { if localAdminDept == nil { panic("implement not found for interface IAdminDept, forgot register?") @@ -253,15 +231,26 @@ func RegisterAdminMember(i IAdminMember) { localAdminMember = i } -func AdminNotice() IAdminNotice { - if localAdminNotice == nil { - panic("implement not found for interface IAdminNotice, forgot register?") +func AdminMemberPost() IAdminMemberPost { + if localAdminMemberPost == nil { + panic("implement not found for interface IAdminMemberPost, forgot register?") } - return localAdminNotice + return localAdminMemberPost } -func RegisterAdminNotice(i IAdminNotice) { - localAdminNotice = i +func RegisterAdminMemberPost(i IAdminMemberPost) { + localAdminMemberPost = i +} + +func AdminOrder() IAdminOrder { + if localAdminOrder == nil { + panic("implement not found for interface IAdminOrder, forgot register?") + } + return localAdminOrder +} + +func RegisterAdminOrder(i IAdminOrder) { + localAdminOrder = i } func AdminPost() IAdminPost { @@ -274,3 +263,14 @@ func AdminPost() IAdminPost { func RegisterAdminPost(i IAdminPost) { localAdminPost = i } + +func AdminRole() IAdminRole { + if localAdminRole == nil { + panic("implement not found for interface IAdminRole, forgot register?") + } + return localAdminRole +} + +func RegisterAdminRole(i IAdminRole) { + localAdminRole = i +} diff --git a/server/internal/service/middleware.go b/server/internal/service/middleware.go index f3be3ef..5ae52c9 100644 --- a/server/internal/service/middleware.go +++ b/server/internal/service/middleware.go @@ -20,7 +20,7 @@ type ( Blacklist(r *ghttp.Request) Develop(r *ghttp.Request) ResponseHandler(r *ghttp.Request) - WebSocketToken(r *ghttp.Request) + WebSocketAuth(r *ghttp.Request) } ) diff --git a/server/internal/service/sys.go b/server/internal/service/sys.go index a6fdc77..04c2292 100644 --- a/server/internal/service/sys.go +++ b/server/internal/service/sys.go @@ -17,6 +17,11 @@ import ( ) type ( + ISysAddonsConfig interface { + GetConfigByGroup(ctx context.Context, in sysin.GetAddonsConfigInp) (res *sysin.GetAddonsConfigModel, err error) + ConversionType(ctx context.Context, models *entity.SysAddonsConfig) (value interface{}, err error) + UpdateConfigByGroup(ctx context.Context, in sysin.UpdateAddonsConfigInp) (err error) + } ISysCron interface { StartCron(ctx context.Context) Delete(ctx context.Context, in sysin.CronDeleteInp) (err error) @@ -27,123 +32,6 @@ type ( List(ctx context.Context, in sysin.CronListInp) (list []*sysin.CronListModel, totalCount int, err error) OnlineExec(ctx context.Context, in sysin.OnlineExecInp) (err error) } - ISysBlacklist interface { - Delete(ctx context.Context, in sysin.BlacklistDeleteInp) (err error) - Edit(ctx context.Context, in sysin.BlacklistEditInp) (err error) - Status(ctx context.Context, in sysin.BlacklistStatusInp) (err error) - MaxSort(ctx context.Context, in sysin.BlacklistMaxSortInp) (res *sysin.BlacklistMaxSortModel, err error) - View(ctx context.Context, in sysin.BlacklistViewInp) (res *sysin.BlacklistViewModel, err error) - List(ctx context.Context, in sysin.BlacklistListInp) (list []*sysin.BlacklistListModel, totalCount int, err error) - VariableLoad(ctx context.Context, err error) - Load(ctx context.Context) - } - ISysDictData interface { - Delete(ctx context.Context, in sysin.DictDataDeleteInp) error - Edit(ctx context.Context, in sysin.DictDataEditInp) (err error) - List(ctx context.Context, in sysin.DictDataListInp) (list []*sysin.DictDataListModel, totalCount int, err error) - Select(ctx context.Context, in sysin.DataSelectInp) (list sysin.DataSelectModel, err error) - } - ISysLog interface { - Export(ctx context.Context, in sysin.LogListInp) (err error) - RealWrite(ctx context.Context, log entity.SysLog) (err error) - AutoLog(ctx context.Context) error - AnalysisLog(ctx context.Context) entity.SysLog - View(ctx context.Context, in sysin.LogViewInp) (res *sysin.LogViewModel, err error) - Delete(ctx context.Context, in sysin.LogDeleteInp) (err error) - List(ctx context.Context, in sysin.LogListInp) (list []*sysin.LogListModel, totalCount int, err error) - } - ISysLoginLog interface { - Model(ctx context.Context) *gdb.Model - List(ctx context.Context, in sysin.LoginLogListInp) (list []*sysin.LoginLogListModel, totalCount int, err error) - Export(ctx context.Context, in sysin.LoginLogListInp) (err error) - Delete(ctx context.Context, in sysin.LoginLogDeleteInp) (err error) - View(ctx context.Context, in sysin.LoginLogViewInp) (res *sysin.LoginLogViewModel, err error) - Push(ctx context.Context, in sysin.LoginLogPushInp) - RealWrite(ctx context.Context, models entity.SysLoginLog) (err error) - } - ISysAddons interface { - List(ctx context.Context, in sysin.AddonsListInp) (list []*sysin.AddonsListModel, totalCount int, err error) - Selects(ctx context.Context, in sysin.AddonsSelectsInp) (res *sysin.AddonsSelectsModel, err error) - Build(ctx context.Context, in sysin.AddonsBuildInp) (err error) - Install(ctx context.Context, in sysin.AddonsInstallInp) (err error) - Upgrade(ctx context.Context, in sysin.AddonsUpgradeInp) (err error) - UnInstall(ctx context.Context, in sysin.AddonsUnInstallInp) (err error) - } - ISysConfig interface { - InitConfig(ctx context.Context) - GetLoadTCP(ctx context.Context) (conf *model.TCPConfig, err error) - GetLoadCache(ctx context.Context) (conf *model.CacheConfig, err error) - GetLoadGenerate(ctx context.Context) (conf *model.GenerateConfig, err error) - GetWechat(ctx context.Context) (conf *model.WechatConfig, err error) - GetPay(ctx context.Context) (conf *model.PayConfig, err error) - GetSms(ctx context.Context) (conf *model.SmsConfig, err error) - GetGeo(ctx context.Context) (conf *model.GeoConfig, err error) - GetUpload(ctx context.Context) (conf *model.UploadConfig, err error) - GetSmtp(ctx context.Context) (conf *model.EmailConfig, err error) - GetBasic(ctx context.Context) (conf *model.BasicConfig, err error) - GetLoadSSL(ctx context.Context) (conf *model.SSLConfig, err error) - GetLoadLog(ctx context.Context) (conf *model.LogConfig, err error) - GetLoadServeLog(ctx context.Context) (conf *model.ServeLogConfig, err error) - GetConfigByGroup(ctx context.Context, in sysin.GetConfigInp) (res *sysin.GetConfigModel, err error) - ConversionType(ctx context.Context, models *entity.SysConfig) (value interface{}, err error) - UpdateConfigByGroup(ctx context.Context, in sysin.UpdateConfigInp) (err error) - } - ISysEmsLog interface { - Delete(ctx context.Context, in sysin.EmsLogDeleteInp) (err error) - Edit(ctx context.Context, in sysin.EmsLogEditInp) (err error) - Status(ctx context.Context, in sysin.EmsLogStatusInp) (err error) - View(ctx context.Context, in sysin.EmsLogViewInp) (res *sysin.EmsLogViewModel, err error) - List(ctx context.Context, in sysin.EmsLogListInp) (list []*sysin.EmsLogListModel, totalCount int, err error) - Send(ctx context.Context, in sysin.SendEmsInp) (err error) - GetTemplate(ctx context.Context, template string, config *model.EmailConfig) (val string, err error) - AllowSend(ctx context.Context, models *entity.SysEmsLog, config *model.EmailConfig) (err error) - VerifyCode(ctx context.Context, in sysin.VerifyEmsCodeInp) (err error) - } - ISysGenCodes interface { - Delete(ctx context.Context, in sysin.GenCodesDeleteInp) (err error) - Edit(ctx context.Context, in sysin.GenCodesEditInp) (res *sysin.GenCodesEditModel, err error) - Status(ctx context.Context, in sysin.GenCodesStatusInp) (err error) - MaxSort(ctx context.Context, in sysin.GenCodesMaxSortInp) (res *sysin.GenCodesMaxSortModel, err error) - View(ctx context.Context, in sysin.GenCodesViewInp) (res *sysin.GenCodesViewModel, err error) - List(ctx context.Context, in sysin.GenCodesListInp) (list []*sysin.GenCodesListModel, totalCount int, err error) - Selects(ctx context.Context, in sysin.GenCodesSelectsInp) (res *sysin.GenCodesSelectsModel, err error) - TableSelect(ctx context.Context, in sysin.GenCodesTableSelectInp) (res []*sysin.GenCodesTableSelectModel, err error) - ColumnSelect(ctx context.Context, in sysin.GenCodesColumnSelectInp) (res []*sysin.GenCodesColumnSelectModel, err error) - ColumnList(ctx context.Context, in sysin.GenCodesColumnListInp) (res []*sysin.GenCodesColumnListModel, err error) - Preview(ctx context.Context, in sysin.GenCodesPreviewInp) (res *sysin.GenCodesPreviewModel, err error) - Build(ctx context.Context, in sysin.GenCodesBuildInp) (err error) - } - ISysProvinces interface { - Tree(ctx context.Context) (list []g.Map, err error) - Delete(ctx context.Context, in sysin.ProvincesDeleteInp) (err error) - Edit(ctx context.Context, in sysin.ProvincesEditInp) (err error) - Status(ctx context.Context, in sysin.ProvincesStatusInp) (err error) - MaxSort(ctx context.Context, in sysin.ProvincesMaxSortInp) (res *sysin.ProvincesMaxSortModel, err error) - View(ctx context.Context, in sysin.ProvincesViewInp) (res *sysin.ProvincesViewModel, err error) - List(ctx context.Context, in sysin.ProvincesListInp) (list []*sysin.ProvincesListModel, totalCount int, err error) - ChildrenList(ctx context.Context, in sysin.ProvincesChildrenListInp) (list []*sysin.ProvincesChildrenListModel, totalCount int, err error) - UniqueId(ctx context.Context, in sysin.ProvincesUniqueIdInp) (res *sysin.ProvincesUniqueIdModel, err error) - Select(ctx context.Context, in sysin.ProvincesSelectInp) (res *sysin.ProvincesSelectModel, err error) - } - ISysAttachment interface { - Model(ctx context.Context, option ...*handler.Option) *gdb.Model - Delete(ctx context.Context, in sysin.AttachmentDeleteInp) (err error) - Edit(ctx context.Context, in sysin.AttachmentEditInp) (err error) - Status(ctx context.Context, in sysin.AttachmentStatusInp) (err error) - MaxSort(ctx context.Context, in sysin.AttachmentMaxSortInp) (res *sysin.AttachmentMaxSortModel, err error) - View(ctx context.Context, in sysin.AttachmentViewInp) (res *sysin.AttachmentViewModel, err error) - List(ctx context.Context, in sysin.AttachmentListInp) (list []*sysin.AttachmentListModel, totalCount int, err error) - Add(ctx context.Context, meta *sysin.UploadFileMeta, fullPath, drive string) (models *entity.SysAttachment, err error) - } - ISysCronGroup interface { - Delete(ctx context.Context, in sysin.CronGroupDeleteInp) (err error) - Edit(ctx context.Context, in sysin.CronGroupEditInp) (err error) - Status(ctx context.Context, in sysin.CronGroupStatusInp) (err error) - MaxSort(ctx context.Context, in sysin.CronGroupMaxSortInp) (res *sysin.CronGroupMaxSortModel, err error) - View(ctx context.Context, in sysin.CronGroupViewInp) (res *sysin.CronGroupViewModel, err error) - List(ctx context.Context, in sysin.CronGroupListInp) (list []*sysin.CronGroupListModel, totalCount int, err error) - Select(ctx context.Context, in sysin.CronGroupSelectInp) (res *sysin.CronGroupSelectModel, err error) - } ISysCurdDemo interface { Model(ctx context.Context, option ...*handler.Option) *gdb.Model List(ctx context.Context, in sysin.CurdDemoListInp) (list []*sysin.CurdDemoListModel, totalCount int, err error) @@ -161,13 +49,40 @@ type ( Edit(ctx context.Context, in sysin.DictTypeEditInp) (err error) TreeSelect(ctx context.Context, in sysin.DictTreeSelectInp) (list []*sysin.DictTypeTree, err error) } - ISysServeLog interface { - Model(ctx context.Context) *gdb.Model - List(ctx context.Context, in sysin.ServeLogListInp) (list []*sysin.ServeLogListModel, totalCount int, err error) - Export(ctx context.Context, in sysin.ServeLogListInp) (err error) - Delete(ctx context.Context, in sysin.ServeLogDeleteInp) (err error) - View(ctx context.Context, in sysin.ServeLogViewInp) (res *sysin.ServeLogViewModel, err error) - RealWrite(ctx context.Context, models entity.SysServeLog) (err error) + ISysCronGroup interface { + Delete(ctx context.Context, in sysin.CronGroupDeleteInp) (err error) + Edit(ctx context.Context, in sysin.CronGroupEditInp) (err error) + Status(ctx context.Context, in sysin.CronGroupStatusInp) (err error) + MaxSort(ctx context.Context, in sysin.CronGroupMaxSortInp) (res *sysin.CronGroupMaxSortModel, err error) + View(ctx context.Context, in sysin.CronGroupViewInp) (res *sysin.CronGroupViewModel, err error) + List(ctx context.Context, in sysin.CronGroupListInp) (list []*sysin.CronGroupListModel, totalCount int, err error) + Select(ctx context.Context, in sysin.CronGroupSelectInp) (res *sysin.CronGroupSelectModel, err error) + } + ISysDictData interface { + Delete(ctx context.Context, in sysin.DictDataDeleteInp) error + Edit(ctx context.Context, in sysin.DictDataEditInp) (err error) + List(ctx context.Context, in sysin.DictDataListInp) (list []*sysin.DictDataListModel, totalCount int, err error) + Select(ctx context.Context, in sysin.DataSelectInp) (list sysin.DataSelectModel, err error) + } + ISysEmsLog interface { + Delete(ctx context.Context, in sysin.EmsLogDeleteInp) (err error) + Edit(ctx context.Context, in sysin.EmsLogEditInp) (err error) + Status(ctx context.Context, in sysin.EmsLogStatusInp) (err error) + View(ctx context.Context, in sysin.EmsLogViewInp) (res *sysin.EmsLogViewModel, err error) + List(ctx context.Context, in sysin.EmsLogListInp) (list []*sysin.EmsLogListModel, totalCount int, err error) + Send(ctx context.Context, in sysin.SendEmsInp) (err error) + GetTemplate(ctx context.Context, template string, config *model.EmailConfig) (val string, err error) + AllowSend(ctx context.Context, models *entity.SysEmsLog, config *model.EmailConfig) (err error) + VerifyCode(ctx context.Context, in sysin.VerifyEmsCodeInp) (err error) + } + ISysLog interface { + Export(ctx context.Context, in sysin.LogListInp) (err error) + RealWrite(ctx context.Context, log entity.SysLog) (err error) + AutoLog(ctx context.Context) error + AnalysisLog(ctx context.Context) entity.SysLog + View(ctx context.Context, in sysin.LogViewInp) (res *sysin.LogViewModel, err error) + Delete(ctx context.Context, in sysin.LogDeleteInp) (err error) + List(ctx context.Context, in sysin.LogListInp) (list []*sysin.LogListModel, totalCount int, err error) } ISysSmsLog interface { Delete(ctx context.Context, in sysin.SmsLogDeleteInp) (err error) @@ -181,88 +96,119 @@ type ( AllowSend(ctx context.Context, models *entity.SysSmsLog, config *model.SmsConfig) (err error) VerifyCode(ctx context.Context, in sysin.VerifyCodeInp) (err error) } - ISysAddonsConfig interface { - GetConfigByGroup(ctx context.Context, in sysin.GetAddonsConfigInp) (res *sysin.GetAddonsConfigModel, err error) - ConversionType(ctx context.Context, models *entity.SysAddonsConfig) (value interface{}, err error) - UpdateConfigByGroup(ctx context.Context, in sysin.UpdateAddonsConfigInp) (err error) + ISysAddons interface { + List(ctx context.Context, in sysin.AddonsListInp) (list []*sysin.AddonsListModel, totalCount int, err error) + Selects(ctx context.Context, in sysin.AddonsSelectsInp) (res *sysin.AddonsSelectsModel, err error) + Build(ctx context.Context, in sysin.AddonsBuildInp) (err error) + Install(ctx context.Context, in sysin.AddonsInstallInp) (err error) + Upgrade(ctx context.Context, in sysin.AddonsUpgradeInp) (err error) + UnInstall(ctx context.Context, in sysin.AddonsUnInstallInp) (err error) + } + ISysConfig interface { + InitConfig(ctx context.Context) + GetLoadTCP(ctx context.Context) (conf *model.TCPConfig, err error) + GetLoadCache(ctx context.Context) (conf *model.CacheConfig, err error) + GetLoadGenerate(ctx context.Context) (conf *model.GenerateConfig, err error) + GetLoadToken(ctx context.Context) (conf *model.TokenConfig, err error) + GetWechat(ctx context.Context) (conf *model.WechatConfig, err error) + GetPay(ctx context.Context) (conf *model.PayConfig, err error) + GetSms(ctx context.Context) (conf *model.SmsConfig, err error) + GetGeo(ctx context.Context) (conf *model.GeoConfig, err error) + GetUpload(ctx context.Context) (conf *model.UploadConfig, err error) + GetSmtp(ctx context.Context) (conf *model.EmailConfig, err error) + GetBasic(ctx context.Context) (conf *model.BasicConfig, err error) + GetLoadSSL(ctx context.Context) (conf *model.SSLConfig, err error) + GetLoadLog(ctx context.Context) (conf *model.LogConfig, err error) + GetLoadServeLog(ctx context.Context) (conf *model.ServeLogConfig, err error) + GetConfigByGroup(ctx context.Context, in sysin.GetConfigInp) (res *sysin.GetConfigModel, err error) + ConversionType(ctx context.Context, models *entity.SysConfig) (value interface{}, err error) + UpdateConfigByGroup(ctx context.Context, in sysin.UpdateConfigInp) (err error) + } + ISysLoginLog interface { + Model(ctx context.Context) *gdb.Model + List(ctx context.Context, in sysin.LoginLogListInp) (list []*sysin.LoginLogListModel, totalCount int, err error) + Export(ctx context.Context, in sysin.LoginLogListInp) (err error) + Delete(ctx context.Context, in sysin.LoginLogDeleteInp) (err error) + View(ctx context.Context, in sysin.LoginLogViewInp) (res *sysin.LoginLogViewModel, err error) + Push(ctx context.Context, in sysin.LoginLogPushInp) + RealWrite(ctx context.Context, models entity.SysLoginLog) (err error) + } + ISysProvinces interface { + Tree(ctx context.Context) (list []g.Map, err error) + Delete(ctx context.Context, in sysin.ProvincesDeleteInp) (err error) + Edit(ctx context.Context, in sysin.ProvincesEditInp) (err error) + Status(ctx context.Context, in sysin.ProvincesStatusInp) (err error) + MaxSort(ctx context.Context, in sysin.ProvincesMaxSortInp) (res *sysin.ProvincesMaxSortModel, err error) + View(ctx context.Context, in sysin.ProvincesViewInp) (res *sysin.ProvincesViewModel, err error) + List(ctx context.Context, in sysin.ProvincesListInp) (list []*sysin.ProvincesListModel, totalCount int, err error) + ChildrenList(ctx context.Context, in sysin.ProvincesChildrenListInp) (list []*sysin.ProvincesChildrenListModel, totalCount int, err error) + UniqueId(ctx context.Context, in sysin.ProvincesUniqueIdInp) (res *sysin.ProvincesUniqueIdModel, err error) + Select(ctx context.Context, in sysin.ProvincesSelectInp) (res *sysin.ProvincesSelectModel, err error) + } + ISysServeLog interface { + Model(ctx context.Context) *gdb.Model + List(ctx context.Context, in sysin.ServeLogListInp) (list []*sysin.ServeLogListModel, totalCount int, err error) + Export(ctx context.Context, in sysin.ServeLogListInp) (err error) + Delete(ctx context.Context, in sysin.ServeLogDeleteInp) (err error) + View(ctx context.Context, in sysin.ServeLogViewInp) (res *sysin.ServeLogViewModel, err error) + RealWrite(ctx context.Context, models entity.SysServeLog) (err error) + } + ISysAttachment interface { + Model(ctx context.Context, option ...*handler.Option) *gdb.Model + Delete(ctx context.Context, in sysin.AttachmentDeleteInp) (err error) + Edit(ctx context.Context, in sysin.AttachmentEditInp) (err error) + Status(ctx context.Context, in sysin.AttachmentStatusInp) (err error) + MaxSort(ctx context.Context, in sysin.AttachmentMaxSortInp) (res *sysin.AttachmentMaxSortModel, err error) + View(ctx context.Context, in sysin.AttachmentViewInp) (res *sysin.AttachmentViewModel, err error) + List(ctx context.Context, in sysin.AttachmentListInp) (list []*sysin.AttachmentListModel, totalCount int, err error) + Add(ctx context.Context, meta *sysin.UploadFileMeta, fullPath, drive string) (models *entity.SysAttachment, err error) + } + ISysBlacklist interface { + Delete(ctx context.Context, in sysin.BlacklistDeleteInp) (err error) + Edit(ctx context.Context, in sysin.BlacklistEditInp) (err error) + Status(ctx context.Context, in sysin.BlacklistStatusInp) (err error) + MaxSort(ctx context.Context, in sysin.BlacklistMaxSortInp) (res *sysin.BlacklistMaxSortModel, err error) + View(ctx context.Context, in sysin.BlacklistViewInp) (res *sysin.BlacklistViewModel, err error) + List(ctx context.Context, in sysin.BlacklistListInp) (list []*sysin.BlacklistListModel, totalCount int, err error) + VariableLoad(ctx context.Context, err error) + Load(ctx context.Context) + } + ISysGenCodes interface { + Delete(ctx context.Context, in sysin.GenCodesDeleteInp) (err error) + Edit(ctx context.Context, in sysin.GenCodesEditInp) (res *sysin.GenCodesEditModel, err error) + Status(ctx context.Context, in sysin.GenCodesStatusInp) (err error) + MaxSort(ctx context.Context, in sysin.GenCodesMaxSortInp) (res *sysin.GenCodesMaxSortModel, err error) + View(ctx context.Context, in sysin.GenCodesViewInp) (res *sysin.GenCodesViewModel, err error) + List(ctx context.Context, in sysin.GenCodesListInp) (list []*sysin.GenCodesListModel, totalCount int, err error) + Selects(ctx context.Context, in sysin.GenCodesSelectsInp) (res *sysin.GenCodesSelectsModel, err error) + TableSelect(ctx context.Context, in sysin.GenCodesTableSelectInp) (res []*sysin.GenCodesTableSelectModel, err error) + ColumnSelect(ctx context.Context, in sysin.GenCodesColumnSelectInp) (res []*sysin.GenCodesColumnSelectModel, err error) + ColumnList(ctx context.Context, in sysin.GenCodesColumnListInp) (res []*sysin.GenCodesColumnListModel, err error) + Preview(ctx context.Context, in sysin.GenCodesPreviewInp) (res *sysin.GenCodesPreviewModel, err error) + Build(ctx context.Context, in sysin.GenCodesBuildInp) (err error) } ) var ( - localSysProvinces ISysProvinces - localSysAttachment ISysAttachment - localSysConfig ISysConfig localSysEmsLog ISysEmsLog - localSysGenCodes ISysGenCodes - localSysServeLog ISysServeLog + localSysLog ISysLog localSysSmsLog ISysSmsLog - localSysAddonsConfig ISysAddonsConfig localSysCronGroup ISysCronGroup + localSysDictData ISysDictData + localSysLoginLog ISysLoginLog + localSysProvinces ISysProvinces + localSysServeLog ISysServeLog + localSysAddons ISysAddons + localSysConfig ISysConfig + localSysGenCodes ISysGenCodes + localSysAttachment ISysAttachment + localSysBlacklist ISysBlacklist localSysCurdDemo ISysCurdDemo localSysDictType ISysDictType + localSysAddonsConfig ISysAddonsConfig localSysCron ISysCron - localSysLoginLog ISysLoginLog - localSysAddons ISysAddons - localSysBlacklist ISysBlacklist - localSysDictData ISysDictData - localSysLog ISysLog ) -func SysAttachment() ISysAttachment { - if localSysAttachment == nil { - panic("implement not found for interface ISysAttachment, forgot register?") - } - return localSysAttachment -} - -func RegisterSysAttachment(i ISysAttachment) { - localSysAttachment = i -} - -func SysConfig() ISysConfig { - if localSysConfig == nil { - panic("implement not found for interface ISysConfig, forgot register?") - } - return localSysConfig -} - -func RegisterSysConfig(i ISysConfig) { - localSysConfig = i -} - -func SysEmsLog() ISysEmsLog { - if localSysEmsLog == nil { - panic("implement not found for interface ISysEmsLog, forgot register?") - } - return localSysEmsLog -} - -func RegisterSysEmsLog(i ISysEmsLog) { - localSysEmsLog = i -} - -func SysGenCodes() ISysGenCodes { - if localSysGenCodes == nil { - panic("implement not found for interface ISysGenCodes, forgot register?") - } - return localSysGenCodes -} - -func RegisterSysGenCodes(i ISysGenCodes) { - localSysGenCodes = i -} - -func SysProvinces() ISysProvinces { - if localSysProvinces == nil { - panic("implement not found for interface ISysProvinces, forgot register?") - } - return localSysProvinces -} - -func RegisterSysProvinces(i ISysProvinces) { - localSysProvinces = i -} - func SysAddonsConfig() ISysAddonsConfig { if localSysAddonsConfig == nil { panic("implement not found for interface ISysAddonsConfig, forgot register?") @@ -274,15 +220,15 @@ func RegisterSysAddonsConfig(i ISysAddonsConfig) { localSysAddonsConfig = i } -func SysCronGroup() ISysCronGroup { - if localSysCronGroup == nil { - panic("implement not found for interface ISysCronGroup, forgot register?") +func SysCron() ISysCron { + if localSysCron == nil { + panic("implement not found for interface ISysCron, forgot register?") } - return localSysCronGroup + return localSysCron } -func RegisterSysCronGroup(i ISysCronGroup) { - localSysCronGroup = i +func RegisterSysCron(i ISysCron) { + localSysCron = i } func SysCurdDemo() ISysCurdDemo { @@ -307,59 +253,15 @@ func RegisterSysDictType(i ISysDictType) { localSysDictType = i } -func SysServeLog() ISysServeLog { - if localSysServeLog == nil { - panic("implement not found for interface ISysServeLog, forgot register?") +func SysCronGroup() ISysCronGroup { + if localSysCronGroup == nil { + panic("implement not found for interface ISysCronGroup, forgot register?") } - return localSysServeLog + return localSysCronGroup } -func RegisterSysServeLog(i ISysServeLog) { - localSysServeLog = i -} - -func SysSmsLog() ISysSmsLog { - if localSysSmsLog == nil { - panic("implement not found for interface ISysSmsLog, forgot register?") - } - return localSysSmsLog -} - -func RegisterSysSmsLog(i ISysSmsLog) { - localSysSmsLog = i -} - -func SysCron() ISysCron { - if localSysCron == nil { - panic("implement not found for interface ISysCron, forgot register?") - } - return localSysCron -} - -func RegisterSysCron(i ISysCron) { - localSysCron = i -} - -func SysAddons() ISysAddons { - if localSysAddons == nil { - panic("implement not found for interface ISysAddons, forgot register?") - } - return localSysAddons -} - -func RegisterSysAddons(i ISysAddons) { - localSysAddons = i -} - -func SysBlacklist() ISysBlacklist { - if localSysBlacklist == nil { - panic("implement not found for interface ISysBlacklist, forgot register?") - } - return localSysBlacklist -} - -func RegisterSysBlacklist(i ISysBlacklist) { - localSysBlacklist = i +func RegisterSysCronGroup(i ISysCronGroup) { + localSysCronGroup = i } func SysDictData() ISysDictData { @@ -373,6 +275,17 @@ func RegisterSysDictData(i ISysDictData) { localSysDictData = i } +func SysEmsLog() ISysEmsLog { + if localSysEmsLog == nil { + panic("implement not found for interface ISysEmsLog, forgot register?") + } + return localSysEmsLog +} + +func RegisterSysEmsLog(i ISysEmsLog) { + localSysEmsLog = i +} + func SysLog() ISysLog { if localSysLog == nil { panic("implement not found for interface ISysLog, forgot register?") @@ -384,6 +297,39 @@ func RegisterSysLog(i ISysLog) { localSysLog = i } +func SysSmsLog() ISysSmsLog { + if localSysSmsLog == nil { + panic("implement not found for interface ISysSmsLog, forgot register?") + } + return localSysSmsLog +} + +func RegisterSysSmsLog(i ISysSmsLog) { + localSysSmsLog = i +} + +func SysAddons() ISysAddons { + if localSysAddons == nil { + panic("implement not found for interface ISysAddons, forgot register?") + } + return localSysAddons +} + +func RegisterSysAddons(i ISysAddons) { + localSysAddons = i +} + +func SysConfig() ISysConfig { + if localSysConfig == nil { + panic("implement not found for interface ISysConfig, forgot register?") + } + return localSysConfig +} + +func RegisterSysConfig(i ISysConfig) { + localSysConfig = i +} + func SysLoginLog() ISysLoginLog { if localSysLoginLog == nil { panic("implement not found for interface ISysLoginLog, forgot register?") @@ -394,3 +340,58 @@ func SysLoginLog() ISysLoginLog { func RegisterSysLoginLog(i ISysLoginLog) { localSysLoginLog = i } + +func SysProvinces() ISysProvinces { + if localSysProvinces == nil { + panic("implement not found for interface ISysProvinces, forgot register?") + } + return localSysProvinces +} + +func RegisterSysProvinces(i ISysProvinces) { + localSysProvinces = i +} + +func SysServeLog() ISysServeLog { + if localSysServeLog == nil { + panic("implement not found for interface ISysServeLog, forgot register?") + } + return localSysServeLog +} + +func RegisterSysServeLog(i ISysServeLog) { + localSysServeLog = i +} + +func SysAttachment() ISysAttachment { + if localSysAttachment == nil { + panic("implement not found for interface ISysAttachment, forgot register?") + } + return localSysAttachment +} + +func RegisterSysAttachment(i ISysAttachment) { + localSysAttachment = i +} + +func SysBlacklist() ISysBlacklist { + if localSysBlacklist == nil { + panic("implement not found for interface ISysBlacklist, forgot register?") + } + return localSysBlacklist +} + +func RegisterSysBlacklist(i ISysBlacklist) { + localSysBlacklist = i +} + +func SysGenCodes() ISysGenCodes { + if localSysGenCodes == nil { + panic("implement not found for interface ISysGenCodes, forgot register?") + } + return localSysGenCodes +} + +func RegisterSysGenCodes(i ISysGenCodes) { + localSysGenCodes = i +} diff --git a/server/manifest/config/config.example.yaml b/server/manifest/config/config.example.yaml index 1e89d3d..43dbe04 100644 --- a/server/manifest/config/config.example.yaml +++ b/server/manifest/config/config.example.yaml @@ -153,12 +153,14 @@ cache: fileDir: "./storage/cache" # 文件缓存路径,adapter=file时必填 -#JWT -jwt: - version: "1.0" # 版本号 - expires: "864000" # 有效期,单位:秒 - sign: "hotgo" # 秘钥,考虑安全问题请修改默认值 - multiPort: true # 是否允许多端登录,默认为true +# 登录令牌 +token: + secretKey: "hotgo123" # 令牌加密秘钥,考虑安全问题生产环境中请修改默认值 + expires: 604800 # 令牌有效期,单位:秒。默认7天 + autoRefresh: true # 是否开启自动刷新过期时间, false|true 默认为true + refreshInterval: 86400 # 刷新间隔,单位:秒。必须小于expires,否则无法触发。默认1天内只允许刷新一次 + maxRefreshTimes: 30 # 最大允许刷新次数,-1不限制。默认30次 + multiLogin: true # 是否允许多端登录, false|true 默认为true #消息队列 diff --git a/server/resource/generate/default/addon/router/websocket.go.template b/server/resource/generate/default/addon/router/websocket.go.template index 42d0bae..bf4abc5 100644 --- a/server/resource/generate/default/addon/router/websocket.go.template +++ b/server/resource/generate/default/addon/router/websocket.go.template @@ -25,7 +25,7 @@ func WebSocket(ctx context.Context, group *ghttp.RouterGroup) { websocket.Index, ) // ws连接中间件 - group.Middleware(service.Middleware().WebSocketToken) + group.Middleware(service.Middleware().WebSocketAuth) group.Bind( // 需要验证的路由 // .. diff --git a/server/storage/data/hotgo.sql b/server/storage/data/hotgo.sql index 90114c7..9aabd20 100644 --- a/server/storage/data/hotgo.sql +++ b/server/storage/data/hotgo.sql @@ -3,7 +3,7 @@ -- https://www.phpmyadmin.net/ -- -- 主机: localhost:3306 --- 生成日期: 2023-05-11 15:56:13 +-- 生成日期: 2023-05-12 15:45:52 -- 服务器版本: 5.7.38-log -- PHP 版本: 5.6.40 @@ -171,7 +171,6 @@ CREATE TABLE `hg_admin_member` ( `username` varchar(20) NOT NULL DEFAULT '' COMMENT '帐号', `password_hash` char(32) NOT NULL DEFAULT '' COMMENT '密码', `salt` char(16) NOT NULL COMMENT '密码盐', - `auth_key` char(32) NOT NULL DEFAULT '' COMMENT '授权令牌', `password_reset_token` varchar(150) DEFAULT '' COMMENT '密码重置令牌', `integral` decimal(10,2) UNSIGNED DEFAULT '0.00' COMMENT '积分', `balance` decimal(10,2) UNSIGNED DEFAULT '0.00' COMMENT '余额', @@ -198,10 +197,10 @@ CREATE TABLE `hg_admin_member` ( -- 转存表中的数据 `hg_admin_member` -- -INSERT INTO `hg_admin_member` (`id`, `dept_id`, `role_id`, `real_name`, `username`, `password_hash`, `salt`, `auth_key`, `password_reset_token`, `integral`, `balance`, `avatar`, `sex`, `qq`, `email`, `mobile`, `birthday`, `city_id`, `address`, `pid`, `level`, `tree`, `cash`, `last_active_at`, `remark`, `status`, `created_at`, `updated_at`) VALUES -(1, 100, 1, '孟帅', 'admin', 'a7c588fffeb2c1d99b29879d7fe97c78', '6541561', '72c79855b0e94d4503e80a9fe064c645', '', '89.00', '99391.78', 'http://bufanyun.cn-bj.ufileos.com/hotgo/attachment/2023-02-09/cqdq8er9nfkchdopav.png', 1, '133814250', '133814250@qq.com', '15303830571', '2016-04-16', 410172, '莲花街001号', 0, 1, '', '{\"name\": \"孟帅\", \"account\": \"15303830571\", \"payeeCode\": \"http://bufanyun.cn-bj.ufileos.com/hotgo/attachment/2023-02-09/cqdq8mqal5isvcb58g.jpg\"}', '2023-05-11 15:52:51', NULL, 1, '2021-02-12 17:59:45', '2023-05-11 15:52:51'), -(3, 100, 2, '测试账号', 'test', 'a7c588fffeb2c1d99b29879d7fe97c78', '6541561', '33db4202dd10e2c670bfdfb16cd2ecd5', '', '0.00', '4.00', 'http://alioss.qvnidaye.com//images/2021/03/12/image_1615529198_vMK4kwq2.jpg', 1, '', 'c@qq.cc', '15303888888', '2016-04-13', 371100, '大潮街道666号', 1, 2, 'tr_1 ', NULL, '2023-04-29 10:20:16', '', 1, '2022-02-11 17:59:45', '2023-04-29 10:20:16'), -(8, 101, 200, 'ameng', 'ameng', '382df3b083a27886edb94e669a857c33', 'hfuUEb', 'a3197f39fae36471b2be8f2e86d8a91e', '', '11.00', '3.22', '', 1, '', '', '', NULL, 0, '', 1, 2, 'tr_1 ', NULL, '2023-04-30 20:04:06', '', 1, '2023-02-03 17:34:31', '2023-04-30 20:04:06'); +INSERT INTO `hg_admin_member` (`id`, `dept_id`, `role_id`, `real_name`, `username`, `password_hash`, `salt`, `password_reset_token`, `integral`, `balance`, `avatar`, `sex`, `qq`, `email`, `mobile`, `birthday`, `city_id`, `address`, `pid`, `level`, `tree`, `cash`, `last_active_at`, `remark`, `status`, `created_at`, `updated_at`) VALUES +(1, 100, 1, '孟帅', 'admin', 'a7c588fffeb2c1d99b29879d7fe97c78', '6541561', '', '89.00', '99391.78', 'http://bufanyun.cn-bj.ufileos.com/hotgo/attachment/2023-02-09/cqdq8er9nfkchdopav.png', 1, '133814250', '133814250@qq.com', '15303830571', '2016-04-16', 410172, '莲花街001号', 0, 1, '', '{\"name\": \"孟帅\", \"account\": \"15303830571\", \"payeeCode\": \"http://bufanyun.cn-bj.ufileos.com/hotgo/attachment/2023-02-09/cqdq8mqal5isvcb58g.jpg\"}', '2023-05-12 15:44:12', NULL, 1, '2021-02-12 17:59:45', '2023-05-12 15:44:12'), +(3, 100, 2, '测试账号', 'test', 'a7c588fffeb2c1d99b29879d7fe97c78', '6541561', '', '0.00', '4.00', 'http://alioss.qvnidaye.com//images/2021/03/12/image_1615529198_vMK4kwq2.jpg', 1, '', 'c@qq.cc', '15303888888', '2016-04-13', 371100, '大潮街道666号', 1, 2, 'tr_1 ', NULL, '2023-04-29 10:20:16', '', 1, '2022-02-11 17:59:45', '2023-04-29 10:20:16'), +(8, 101, 200, 'ameng', 'ameng', '382df3b083a27886edb94e669a857c33', 'hfuUEb', '', '11.00', '3.22', '', 1, '', '', '', NULL, 0, '', 1, 2, 'tr_1 ', NULL, '2023-04-30 20:04:06', '', 1, '2023-02-03 17:34:31', '2023-04-30 20:04:06'); -- -------------------------------------------------------- diff --git a/server/utility/auth/auth.go b/server/utility/auth/auth.go deleted file mode 100644 index 2e3f45f..0000000 --- a/server/utility/auth/auth.go +++ /dev/null @@ -1,45 +0,0 @@ -// Package auth -// @Link https://github.com/bufanyun/hotgo -// @Copyright Copyright (c) 2023 HotGo CLI -// @Author Ms <133814250@qq.com> -// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE -// -package auth - -import ( - "context" - "github.com/gogf/gf/v2/frame/g" - "hotgo/utility/validate" -) - -// IsExceptAuth 是否是不需要验证权限的路由地址 -func IsExceptAuth(ctx context.Context, path string) bool { - var pathList []string - - except := g.Cfg().MustGet(ctx, "router.admin.exceptAuth") - pathList = except.Strings() - - for i := 0; i < len(pathList); i++ { - if validate.InSliceExistStr(pathList[i], path) { - return true - } - } - - return false -} - -// IsExceptLogin 是否是不需要登录的路由地址 -func IsExceptLogin(ctx context.Context, path string) bool { - var pathList []string - - except := g.Cfg().MustGet(ctx, "router.admin.exceptLogin") - pathList = except.Strings() - - for i := 0; i < len(pathList); i++ { - if validate.InSliceExistStr(pathList[i], path) { - return true - } - } - - return false -} diff --git a/server/utility/format/format.go b/server/utility/format/format.go index 27f70be..576874c 100644 --- a/server/utility/format/format.go +++ b/server/utility/format/format.go @@ -3,13 +3,13 @@ // @Copyright Copyright (c) 2023 HotGo CLI // @Author Ms <133814250@qq.com> // @License https://github.com/bufanyun/hotgo/blob/master/LICENSE -// package format import ( "fmt" - "github.com/shopspring/decimal" + "github.com/gogf/gf/v2/util/gconv" "math" + "strconv" ) // RoundInt64 四舍五入 @@ -19,22 +19,19 @@ func RoundInt64(x float64) int64 { // Round2String 四舍五入保留小数,默认2位 func Round2String(value float64, args ...interface{}) (v string) { - var places int32 = 2 + var places = 2 if len(args) > 0 { - places = args[0].(int32) + places = gconv.Int(args[0]) } - return decimal.NewFromFloat(value).Round(places).String() + cDig := strconv.Itoa(places) + val := fmt.Sprintf("%0."+cDig+"f", value) + return val } // Round2Float64 四舍五入保留小数,默认2位 func Round2Float64(value float64, args ...interface{}) (v float64) { - var places int32 = 2 - if len(args) > 0 { - places = args[0].(int32) - } - - return decimal.NewFromFloat(value).Round(places).InexactFloat64() + return gconv.Float64(Round2String(value, args...)) } // FileSize 字节的单位转换 保留两位小数 diff --git a/web/package.json b/web/package.json index e4fdcb0..a7a3b36 100644 --- a/web/package.json +++ b/web/package.json @@ -1,6 +1,6 @@ { "name": "hotgo", - "version": "2.6.7", + "version": "2.6.10", "author": { "name": "MengShuai", "email": "133814250@qq.com", diff --git a/web/src/App.vue b/web/src/App.vue index db690d2..54eeeb9 100644 --- a/web/src/App.vue +++ b/web/src/App.vue @@ -24,7 +24,7 @@ import { useLockscreenStore } from '@/store/modules/lockscreen'; import { useRoute } from 'vue-router'; import { useDesignSettingStore } from '@/store/modules/designSetting'; - import { lighten } from '@/utils/index'; + import { lighten } from '@/utils'; const route = useRoute(); const useLockscreen = useLockscreenStore(); diff --git a/web/src/api/system/user.ts b/web/src/api/system/user.ts index 070ddfa..2cd2bab 100644 --- a/web/src/api/system/user.ts +++ b/web/src/api/system/user.ts @@ -101,6 +101,21 @@ export function login(params) { ); } +/** + * @description: 用户注销 + */ +export function logout() { + return http.request( + { + url: ApiEnum.SiteLogout, + method: 'POST', + }, + { + isTransformResponse: false, + } + ); +} + /** * @description: 用户修改密码 */ @@ -116,14 +131,3 @@ export function changePassword(params, uid) { } ); } - -/** - * @description: 用户登出 - */ -export function logout(params) { - return http.request({ - url: '/login/logout', - method: 'POST', - params, - }); -} diff --git a/web/src/enums/apiEnum.ts b/web/src/enums/apiEnum.ts index 909b176..d66c07f 100644 --- a/web/src/enums/apiEnum.ts +++ b/web/src/enums/apiEnum.ts @@ -4,6 +4,7 @@ export enum ApiEnum { // 基础 SiteLogin = '/site/login', // 登录 + SiteLogout = '/site/logout', // 注销 SiteConfig = '/site/config', // 配置信息 // 用户 diff --git a/web/src/layout/components/Header/index.vue b/web/src/layout/components/Header/index.vue index f9fbec9..88acd19 100644 --- a/web/src/layout/components/Header/index.vue +++ b/web/src/layout/components/Header/index.vue @@ -301,16 +301,16 @@ }); }; - // 退出登录 + // 注销登录 const doLogout = () => { dialog.info({ title: '提示', - content: '您确定要退出登录吗', + content: '您确定要注销登录吗', positiveText: '确定', negativeText: '取消', onPositiveClick: () => { userStore.logout().then(() => { - message.success('成功退出登录'); + message.success('成功注销登录'); // 移除标签页 localStorage.removeItem(TABS_ROUTES); router @@ -377,7 +377,7 @@ key: 1, }, { - label: '退出登录', + label: '注销登录', key: 2, }, ]; diff --git a/web/src/store/modules/user.ts b/web/src/store/modules/user.ts index 38a1e42..ceca865 100644 --- a/web/src/store/modules/user.ts +++ b/web/src/store/modules/user.ts @@ -3,7 +3,7 @@ import { createStorage, storage } from '@/utils/Storage'; import { store } from '@/store'; import { ACCESS_TOKEN, CURRENT_CONFIG, CURRENT_USER, IS_LOCKSCREEN } from '@/store/mutation-types'; import { ResultEnum } from '@/enums/httpEnum'; -import { getConfig, getUserInfo, login } from '@/api/system/user'; +import { getConfig, getUserInfo, login, logout } from '@/api/system/user'; const Storage = createStorage({ storage: localStorage }); export interface UserInfoState { @@ -114,7 +114,7 @@ export const useUserStore = defineStore({ const response = await login(userInfo); const { data, code } = response; if (code === ResultEnum.SUCCESS) { - const ex = 7 * 24 * 60 * 60 * 1000; + const ex = 30 * 24 * 60 * 60 * 1000; storage.set(ACCESS_TOKEN, data.token, ex); storage.set(CURRENT_USER, data, ex); storage.set(IS_LOCKSCREEN, false); @@ -168,11 +168,19 @@ export const useUserStore = defineStore({ }, // 登出 async logout() { - this.setPermissions([]); - this.setUserInfo(null); - storage.remove(ACCESS_TOKEN); - storage.remove(CURRENT_USER); - return Promise.resolve(''); + try { + const response = await logout(); + const { code } = response; + if (code === ResultEnum.SUCCESS) { + this.setPermissions([]); + this.setUserInfo(null); + storage.remove(ACCESS_TOKEN); + storage.remove(CURRENT_USER); + } + return Promise.resolve(response); + } catch (e) { + return Promise.reject(e); + } }, }, }); diff --git a/web/src/utils/http/axios/index.ts b/web/src/utils/http/axios/index.ts index 62f4266..d2c25e9 100644 --- a/web/src/utils/http/axios/index.ts +++ b/web/src/utils/http/axios/index.ts @@ -105,10 +105,10 @@ const transform: AxiosTransform = { const LoginPath = PageEnum.BASE_LOGIN; if (router.currentRoute.value?.name === LoginName) return; // 到登录页 - errorMsg = '登录超时,请重新登录!'; + errorMsg = message ?? '登录超时,请重新登录!'; $dialog.warning({ title: '提示', - content: '登录身份已失效,请重新登录!', + content: errorMsg, // '登录身份已失效,请重新登录!', positiveText: '确定', //negativeText: '取消', closable: false, diff --git a/web/src/views/home/account/SafetySetting.vue b/web/src/views/home/account/SafetySetting.vue index 6451d5a..6ebf8d1 100644 --- a/web/src/views/home/account/SafetySetting.vue +++ b/web/src/views/home/account/SafetySetting.vue @@ -203,7 +203,7 @@ message.success('更新成功'); userStore.logout().then(() => { - message.success('成功退出登录'); + message.success('成功注销登录'); // 移除标签页 localStorage.removeItem(TABS_ROUTES); router diff --git a/web/src/views/home/account/ThirdBind.vue b/web/src/views/home/account/ThirdBind.vue index 94dd1e3..b9fea71 100644 --- a/web/src/views/home/account/ThirdBind.vue +++ b/web/src/views/home/account/ThirdBind.vue @@ -193,7 +193,7 @@ message.success('更新成功'); userStore.logout().then(() => { - message.success('成功退出登录'); + message.success('成功注销登录'); // 移除标签页 localStorage.removeItem(TABS_ROUTES); router diff --git a/web/src/views/monitor/online/columns.ts b/web/src/views/monitor/online/columns.ts index 2cc569d..2bcb636 100644 --- a/web/src/views/monitor/online/columns.ts +++ b/web/src/views/monitor/online/columns.ts @@ -1,6 +1,6 @@ import { h } from 'vue'; import { NAvatar, NTag } from 'naive-ui'; -import { timestampToTime, formatBefore, formatAfter } from '@/utils/dateUtil'; +import { timestampToTime, formatBefore } from '@/utils/dateUtil'; export const columns = [ { @@ -31,11 +31,11 @@ export const columns = [ return row.app; }, }, - // { - // title: '用户ID', - // key: 'userId', - // width: 100, - // }, + { + title: '用户ID', + key: 'userId', + width: 100, + }, { title: '用户名', key: 'username', @@ -90,14 +90,6 @@ export const columns = [ return row.os; }, }, - { - title: '授权过期', - key: 'expTime', - width: 80, - render: (rows, _) => { - return formatAfter(new Date(rows.expTime * 1000)); - }, - }, { title: '最后活跃', key: 'heartbeatTime',