kra/internal/server/middleware/gin_jwt.go

114 lines
3.1 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package middleware
import (
"errors"
"strconv"
"time"
"kra/pkg/jwt"
"kra/pkg/response"
"kra/pkg/utils"
jwtv5 "github.com/golang-jwt/jwt/v5"
"github.com/gin-gonic/gin"
)
// BlacklistChecker JWT黑名单检查接口
type BlacklistChecker interface {
IsBlacklist(jwt string) bool
}
// JWTConfig JWT中间件配置
type JWTConfig struct {
JWT *jwt.JWT
BlacklistChecker BlacklistChecker
UseMultipoint bool
SetRedisJWT func(token, username string) error
}
// 全局配置(需要在初始化时设置)
var jwtConfig *JWTConfig
// SetJWTConfig 设置JWT配置
func SetJWTConfig(cfg *JWTConfig) {
jwtConfig = cfg
// 同时设置 utils 包的 JWT 实例
if cfg != nil && cfg.JWT != nil {
utils.SetJWT(cfg.JWT)
}
}
// JWTAuth JWT认证中间件与 kra 保持一致)
func JWTAuth() gin.HandlerFunc {
return func(c *gin.Context) {
// 我们这里jwt鉴权取头部信息 x-token 登录时回返回token信息 这里前端需要把token存储到cookie或者本地localStorage中 不过需要跟后端协商过期时间 可以约定刷新令牌或者重新登录
token := utils.GetToken(c)
if token == "" {
response.NoAuth("未登录或非法访问,请登录", c)
c.Abort()
return
}
// 检查黑名单
if jwtConfig != nil && jwtConfig.BlacklistChecker != nil && jwtConfig.BlacklistChecker.IsBlacklist(token) {
response.NoAuth("您的帐户异地登陆或令牌失效", c)
utils.ClearToken(c)
c.Abort()
return
}
if jwtConfig == nil || jwtConfig.JWT == nil {
response.NoAuth("JWT配置错误", c)
c.Abort()
return
}
// parseToken 解析token包含的信息
claims, err := jwtConfig.JWT.ParseToken(token)
if err != nil {
if errors.Is(err, jwt.ErrTokenExpired) {
response.NoAuth("登录已过期,请重新登录", c)
utils.ClearToken(c)
c.Abort()
return
}
response.NoAuth(err.Error(), c)
utils.ClearToken(c)
c.Abort()
return
}
// 已登录用户被管理员禁用 需要使该用户的jwt失效 此处比较消耗性能 如果需要 请自行打开
// 用户被删除的逻辑 需要优化 此处比较消耗性能 如果需要 请自行打开
c.Set("claims", claims)
// 检查是否需要刷新token
if claims.ExpiresAt.Unix()-time.Now().Unix() < claims.BufferTime {
claims.ExpiresAt = jwtv5.NewNumericDate(time.Now().Add(jwtConfig.JWT.ExpiresAt))
newToken, newClaims, err := jwtConfig.JWT.CreateTokenByOldToken(token, *claims)
if err == nil {
c.Header("new-token", newToken)
c.Header("new-expires-at", strconv.FormatInt(newClaims.ExpiresAt.Unix(), 10))
utils.SetToken(c, newToken, int(jwtConfig.JWT.ExpiresAt.Seconds()/60))
// 多点登录记录新JWT
if jwtConfig.UseMultipoint && jwtConfig.SetRedisJWT != nil {
_ = jwtConfig.SetRedisJWT(newToken, newClaims.Username)
}
}
}
c.Next()
// 处理后续中间件设置的新token
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))
}
}
}