✨ 新增几大中心功能
This commit is contained in:
34
server/middleware/app_activity.go
Normal file
34
server/middleware/app_activity.go
Normal file
@@ -0,0 +1,34 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
appSvc "git.echol.cn/loser/Go-Web-Template/server/service/app"
|
||||
"git.echol.cn/loser/Go-Web-Template/server/utils"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// AppActivity 玩家端活跃记录:每次请求写 last_active_at(轻量 upsert)
|
||||
func AppActivity() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
c.Next()
|
||||
|
||||
uid := utils.GetUserID(c)
|
||||
if uid == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// 避免极端高频写:简单节流(同请求链路内只写一次即可)
|
||||
if _, exists := c.Get("__app_activity_written"); exists {
|
||||
return
|
||||
}
|
||||
c.Set("__app_activity_written", true)
|
||||
|
||||
// 尽量不阻塞响应:允许轻微延迟
|
||||
go func(userID uint, ip string) {
|
||||
_ = appSvc.AppActivityServiceApp.TouchActive(userID, ip)
|
||||
}(uid, c.ClientIP())
|
||||
|
||||
_ = time.Now() // 保留 import time,避免未来扩展时频繁改动(不影响逻辑)
|
||||
}
|
||||
}
|
||||
74
server/middleware/app_jwt.go
Normal file
74
server/middleware/app_jwt.go
Normal file
@@ -0,0 +1,74 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"git.echol.cn/loser/Go-Web-Template/server/global"
|
||||
"git.echol.cn/loser/Go-Web-Template/server/model/common/response"
|
||||
"git.echol.cn/loser/Go-Web-Template/server/utils"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
)
|
||||
|
||||
// AppJWTAuth 玩家端 JWT 鉴权(仅接受 aud=GVA_APP)
|
||||
func AppJWTAuth() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
token := utils.GetToken(c)
|
||||
if token == "" {
|
||||
response.NoAuth("未登录或非法访问,请登录", c)
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
if isBlacklist(token) {
|
||||
response.NoAuth("您的帐户异地登陆或令牌失效", c)
|
||||
utils.ClearToken(c)
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
j := utils.NewJWT()
|
||||
claims, err := j.ParseToken(token)
|
||||
if err != nil {
|
||||
if errors.Is(err, utils.TokenExpired) {
|
||||
response.NoAuth("登录已过期,请重新登录", c)
|
||||
utils.ClearToken(c)
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
response.NoAuth(err.Error(), c)
|
||||
utils.ClearToken(c)
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
if len(claims.Audience) == 0 || claims.Audience[0] != utils.JWTAudienceApp {
|
||||
response.NoAuth("令牌无效", c)
|
||||
utils.ClearToken(c)
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
c.Set("claims", claims)
|
||||
if claims.ExpiresAt.Unix()-time.Now().Unix() < claims.BufferTime {
|
||||
dr, _ := utils.ParseDuration(global.GVA_CONFIG.JWT.ExpiresTime)
|
||||
claims.ExpiresAt = jwt.NewNumericDate(time.Now().Add(dr))
|
||||
newToken, _ := j.CreateTokenByOldToken(token, *claims)
|
||||
newClaims, _ := j.ParseToken(newToken)
|
||||
c.Header("new-token", newToken)
|
||||
c.Header("new-expires-at", strconv.FormatInt(newClaims.ExpiresAt.Unix(), 10))
|
||||
utils.SetToken(c, newToken, int(dr.Seconds()/60))
|
||||
if global.GVA_CONFIG.System.UseMultipoint {
|
||||
_ = utils.SetRedisJWT(newToken, newClaims.Username)
|
||||
}
|
||||
}
|
||||
c.Next()
|
||||
|
||||
if newToken, exists := c.Get("new-token"); exists {
|
||||
c.Header("new-token", newToken.(string))
|
||||
}
|
||||
if newExpiresAt, exists := c.Get("new-expires-at"); exists {
|
||||
c.Header("new-expires-at", newExpiresAt.(string))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,11 +18,11 @@ func Cors() gin.HandlerFunc {
|
||||
c.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Content-Type, New-Token, New-Expires-At")
|
||||
c.Header("Access-Control-Allow-Credentials", "true")
|
||||
|
||||
// 放行所有OPTIONS方法
|
||||
// 放行所有OPTIONS方法(预检请求不应继续进入路由,否则易落到 404)
|
||||
if method == "OPTIONS" {
|
||||
c.AbortWithStatus(http.StatusNoContent)
|
||||
return
|
||||
}
|
||||
// 处理请求
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
@@ -50,14 +50,14 @@ func CorsByRules() gin.HandlerFunc {
|
||||
// 严格白名单模式且未通过检查,直接拒绝处理请求
|
||||
if whitelist == nil && global.GVA_CONFIG.Cors.Mode == "strict-whitelist" && !(c.Request.Method == "GET" && c.Request.URL.Path == "/health") {
|
||||
c.AbortWithStatus(http.StatusForbidden)
|
||||
} else {
|
||||
// 非严格白名单模式,无论是否通过检查均放行所有 OPTIONS 方法
|
||||
if c.Request.Method == http.MethodOptions {
|
||||
c.AbortWithStatus(http.StatusNoContent)
|
||||
}
|
||||
return
|
||||
}
|
||||
// OPTIONS 预检:直接结束,避免继续匹配路由导致 404
|
||||
if c.Request.Method == http.MethodOptions {
|
||||
c.AbortWithStatus(http.StatusNoContent)
|
||||
return
|
||||
}
|
||||
|
||||
// 处理请求
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,6 +44,14 @@ func JWTAuth() gin.HandlerFunc {
|
||||
return
|
||||
}
|
||||
|
||||
// 后台接口仅接受后台受众 token
|
||||
if len(claims.Audience) == 0 || claims.Audience[0] != utils.JWTAudienceAdmin {
|
||||
response.NoAuth("令牌无效", c)
|
||||
utils.ClearToken(c)
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
// 已登录用户被管理员禁用 需要使该用户的jwt失效 此处比较消耗性能 如果需要 请自行打开
|
||||
// 用户被删除的逻辑 需要优化 此处比较消耗性能 如果需要 请自行打开
|
||||
|
||||
|
||||
Reference in New Issue
Block a user