package middleware import ( "net/http" "strconv" "strings" "time" "kra/internal/conf" "kra/internal/server/httpx" "kra/internal/service" "kra/pkg/adminauth" "github.com/gin-gonic/gin" ) const claimsKey = "admin_claims" func Auth(runtime *conf.Runtime, settings *service.SettingsService) gin.HandlerFunc { return func(c *gin.Context) { token := c.GetHeader("x-token") if token == "" { token, _ = c.Cookie("x-token") } secret := "" config := runtime.Admin() if config != nil && config.Jwt != nil { secret = config.Jwt.SigningKey } claims, err := adminauth.Parse(token, secret) if err != nil { httpx.NoAuth(c, "未登录或非法访问") return } if disabled, checkErr := settings.IsTokenDisabled(c.Request.Context(), token); checkErr != nil || disabled { httpx.NoAuth(c, "登录状态已失效") return } if active, checkErr := settings.ActiveTokenMatches(c.Request.Context(), claims.Username, token); checkErr != nil || !active { httpx.NoAuth(c, "登录状态已失效") return } if claims.ExpiresAt != nil && claims.BufferTime > 0 && time.Until(claims.ExpiresAt.Time) < time.Duration(claims.BufferTime)*time.Second { expires, buffer := 7*24*time.Hour, time.Duration(claims.BufferTime)*time.Second issuer := claims.Issuer config = runtime.Admin() if config != nil && config.Jwt != nil { if config.Jwt.ExpiresTime != nil { expires = config.Jwt.ExpiresTime.AsDuration() } if config.Jwt.BufferTime != nil { buffer = config.Jwt.BufferTime.AsDuration() } if config.Jwt.Issuer != "" { issuer = config.Jwt.Issuer } } newToken, newClaims, refreshErr := adminauth.Generate(secret, issuer, expires, buffer, claims.ID, claims.AuthorityID, claims.UUID, claims.Username, claims.NickName, claims.MustChangePwd) if refreshErr == nil && settings.RotateActiveToken(c.Request.Context(), claims.Username, token, newToken, expires) == nil { c.Header("new-token", newToken) c.Header("new-expires-at", strconv.FormatInt(newClaims.ExpiresAt.Unix(), 10)) httpx.SetTokenCookie(c, newToken, int(expires.Seconds())) } } c.Set(claimsKey, claims) c.Next() } } func Claims(c *gin.Context) *adminauth.Claims { value, _ := c.Get(claimsKey) claims, _ := value.(*adminauth.Claims) return claims } func MustChangePassword() gin.HandlerFunc { return func(c *gin.Context) { claims := Claims(c) if claims == nil || !claims.MustChangePwd { c.Next() return } path := strings.TrimSuffix(c.Request.URL.Path, "/") if strings.HasSuffix(path, "/user/changePassword") || strings.HasSuffix(path, "/user/getUserInfo") || strings.HasSuffix(path, "/jwt/jsonInBlacklist") { c.Next() return } c.AbortWithStatusJSON(http.StatusConflict, httpx.Response{Code: httpx.CodePasswordChangeRequired, Data: gin.H{"needChangePassword": true}, Msg: "请先修改初始密码"}) } }