diff --git a/internal/middleware/auth.go b/internal/middleware/auth.go new file mode 100644 index 0000000..3534e74 --- /dev/null +++ b/internal/middleware/auth.go @@ -0,0 +1,81 @@ +package middleware + +import ( + "errors" + "gin-admin/internal/core/config" + "gin-admin/pkg/auth" + "github.com/gin-gonic/gin" + "github.com/golang-jwt/jwt/v5" + "net/http" + "regexp" + "strings" + + systemmodel "gin-admin/internal/model/system" +) + +// @Author: yv1ing +// @Author: me@yvling.cn +// @Date: 2025/8/28 17:31 +// @Desc: 鉴权中间件 + +func extractBearerToken(c *gin.Context) string { + authorization := c.GetHeader("Authorization") + if authorization == "" { + return "" + } + + parts := strings.SplitN(authorization, " ", 2) + if len(parts) != 2 || !strings.EqualFold(parts[0], "Bearer") || parts[1] == "" { + return "" + } + + return parts[1] +} + +func JwtMiddleware(whitelist []string) gin.HandlerFunc { + var whitelistRegex []*regexp.Regexp + for _, pattern := range whitelist { + re, err := regexp.Compile(pattern) + if err == nil { + whitelistRegex = append(whitelistRegex, re) + } + } + + return func(c *gin.Context) { + path := c.Request.URL.Path + for _, re := range whitelistRegex { + if re.MatchString(path) { + c.Next() + return + } + } + + tokenStr := extractBearerToken(c) + if tokenStr == "" { + c.AbortWithStatusJSON(http.StatusUnauthorized, systemmodel.Response{ + Code: http.StatusUnauthorized, + Info: "请求头Authorization非法或缺失", + }) + return + } + + claims, err := auth.ParseAccessToken(tokenStr, config.Config.SecretKey) + if err != nil { + if errors.Is(err, jwt.ErrTokenExpired) { + c.AbortWithStatusJSON(http.StatusUnauthorized, systemmodel.Response{ + Code: http.StatusUnauthorized, + Info: "Token已过期", + }) + } else { + c.AbortWithStatusJSON(http.StatusUnauthorized, systemmodel.Response{ + Code: http.StatusUnauthorized, + Info: "Token不合法", + }) + } + return + } + + c.Set("UID", claims.ID) + c.Next() + } +} diff --git a/internal/router/router.go b/internal/router/router.go index 33e23cd..a827ff3 100644 --- a/internal/router/router.go +++ b/internal/router/router.go @@ -1,6 +1,7 @@ package router import ( + "gin-admin/internal/middleware" "github.com/gin-gonic/gin" systemapi "gin-admin/internal/api/system" @@ -11,7 +12,13 @@ import ( // @Date: 2025/8/28 16:17 // @Desc: 路由注册入口 +var whitelist = []string{ + `^/api/sys/login$`, +} + func SetupRoutes(eng *gin.Engine) { + eng.Use(middleware.JwtMiddleware(whitelist)) + api := eng.Group("/api") /* 系统内置接口 */ diff --git a/internal/service/system/user_service.go b/internal/service/system/user_service.go index a6777f9..9d5b05c 100644 --- a/internal/service/system/user_service.go +++ b/internal/service/system/user_service.go @@ -2,9 +2,9 @@ package system import ( "errors" - "gin-admin/internal/auth" "gin-admin/internal/core/config" "gin-admin/internal/repository" + "gin-admin/pkg/auth" "gin-admin/pkg/encrypt" "gorm.io/gorm" @@ -31,7 +31,7 @@ func SysUserLogin(username, password string) (string, error) { return "", errors.New("用户已被禁用") } - jwtToken, err := auth.CreateAccessToken(user.ID, user.Username, 3600) + jwtToken, err := auth.CreateAccessToken(user.ID, user.Username, config.Config.SecretKey) if err != nil { return "", errors.New("系统内部错误") } diff --git a/internal/auth/jwt.go b/pkg/auth/jwt.go similarity index 74% rename from internal/auth/jwt.go rename to pkg/auth/jwt.go index 94331f6..098029d 100644 --- a/internal/auth/jwt.go +++ b/pkg/auth/jwt.go @@ -3,7 +3,6 @@ package auth import ( "errors" "fmt" - "gin-admin/internal/core/config" "github.com/golang-jwt/jwt/v5" "time" ) @@ -19,8 +18,8 @@ type AccessClaims struct { jwt.RegisteredClaims } -func CreateAccessToken(ID uint, username string, ttl time.Duration) (string, error) { - jwtSecret := []byte(config.Config.SecretKey) +func CreateAccessToken(ID uint, username string, secretKey string) (string, error) { + jwtSecret := []byte(secretKey) claims := AccessClaims{ ID: ID, @@ -28,7 +27,7 @@ func CreateAccessToken(ID uint, username string, ttl time.Duration) (string, err RegisteredClaims: jwt.RegisteredClaims{ Subject: "gin-admin", IssuedAt: jwt.NewNumericDate(time.Now()), - ExpiresAt: jwt.NewNumericDate(time.Now().Add(ttl)), + ExpiresAt: jwt.NewNumericDate(time.Now().Add(12 * time.Hour)), ID: fmt.Sprintf("%d-%d", ID, time.Now().UnixNano()), }, } @@ -37,8 +36,8 @@ func CreateAccessToken(ID uint, username string, ttl time.Duration) (string, err return token.SignedString(jwtSecret) } -func ParseAccessToken(tokenStr string) (*AccessClaims, error) { - jwtSecret := []byte(config.Config.SecretKey) +func ParseAccessToken(tokenStr, secretKey string) (*AccessClaims, error) { + jwtSecret := []byte(secretKey) token, err := jwt.ParseWithClaims(tokenStr, &AccessClaims{}, func(token *jwt.Token) (interface{}, error) { return jwtSecret, nil })