jwtauthMiddleware.go 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. package middleware
  2. import (
  3. "context"
  4. "net/http"
  5. "strings"
  6. "perms-system-server/internal/consts"
  7. "perms-system-server/internal/loaders"
  8. "perms-system-server/internal/response"
  9. "github.com/golang-jwt/jwt/v4"
  10. "github.com/zeromicro/go-zero/rest/httpx"
  11. )
  12. type contextKey string
  13. const (
  14. ctxKeyUserDetails contextKey = "userDetails"
  15. )
  16. // Claims JWT access token 的 Claims 结构。
  17. type Claims struct {
  18. TokenType string `json:"tokenType"`
  19. UserId int64 `json:"userId"`
  20. Username string `json:"username"`
  21. ProductCode string `json:"productCode"`
  22. MemberType string `json:"memberType"`
  23. TokenVersion int64 `json:"tokenVersion"`
  24. jwt.RegisteredClaims
  25. }
  26. type JwtAuthMiddleware struct {
  27. accessSecret string
  28. loader *loaders.UserDetailsLoader
  29. }
  30. func NewJwtAuthMiddleware(accessSecret string, loader *loaders.UserDetailsLoader) *JwtAuthMiddleware {
  31. return &JwtAuthMiddleware{
  32. accessSecret: accessSecret,
  33. loader: loader,
  34. }
  35. }
  36. func (m *JwtAuthMiddleware) Handle(next http.HandlerFunc) http.HandlerFunc {
  37. return func(w http.ResponseWriter, r *http.Request) {
  38. authHeader := r.Header.Get("Authorization")
  39. if authHeader == "" {
  40. httpx.ErrorCtx(r.Context(), w, response.NewCodeError(401, "未登录"))
  41. return
  42. }
  43. tokenStr := strings.TrimPrefix(authHeader, "Bearer ")
  44. if tokenStr == authHeader {
  45. httpx.ErrorCtx(r.Context(), w, response.NewCodeError(401, "token格式错误"))
  46. return
  47. }
  48. token, err := jwt.ParseWithClaims(tokenStr, &Claims{}, func(token *jwt.Token) (interface{}, error) {
  49. return []byte(m.accessSecret), nil
  50. })
  51. if err != nil || !token.Valid {
  52. httpx.ErrorCtx(r.Context(), w, response.NewCodeError(401, "token无效或已过期"))
  53. return
  54. }
  55. claims, ok := token.Claims.(*Claims)
  56. if !ok || claims.TokenType != consts.TokenTypeAccess {
  57. httpx.ErrorCtx(r.Context(), w, response.NewCodeError(401, "token无效或类型错误"))
  58. return
  59. }
  60. ud := m.loader.Load(r.Context(), claims.UserId, claims.ProductCode)
  61. if ud.Username == "" {
  62. httpx.ErrorCtx(r.Context(), w, response.NewCodeError(401, "用户不存在或已被删除"))
  63. return
  64. }
  65. if ud.Status != consts.StatusEnabled {
  66. httpx.ErrorCtx(r.Context(), w, response.NewCodeError(403, "账号已被冻结"))
  67. return
  68. }
  69. if claims.ProductCode != "" && ud.ProductStatus != consts.StatusEnabled {
  70. httpx.ErrorCtx(r.Context(), w, response.NewCodeError(403, "该产品已被禁用"))
  71. return
  72. }
  73. if claims.ProductCode != "" && !ud.IsSuperAdmin && ud.MemberType == "" {
  74. httpx.ErrorCtx(r.Context(), w, response.NewCodeError(403, "您已不是该产品的有效成员"))
  75. return
  76. }
  77. if claims.TokenVersion != ud.TokenVersion {
  78. httpx.ErrorCtx(r.Context(), w, response.NewCodeError(401, "登录状态已失效,请重新登录"))
  79. return
  80. }
  81. ctx := context.WithValue(r.Context(), ctxKeyUserDetails, ud)
  82. next(w, r.WithContext(ctx))
  83. }
  84. }
  85. // -------- context helpers --------
  86. func WithUserDetails(ctx context.Context, ud *loaders.UserDetails) context.Context {
  87. return context.WithValue(ctx, ctxKeyUserDetails, ud)
  88. }
  89. func GetUserDetails(ctx context.Context) *loaders.UserDetails {
  90. v, _ := ctx.Value(ctxKeyUserDetails).(*loaders.UserDetails)
  91. return v
  92. }
  93. func GetUserId(ctx context.Context) int64 {
  94. if ud := GetUserDetails(ctx); ud != nil {
  95. return ud.UserId
  96. }
  97. return 0
  98. }
  99. func GetProductCode(ctx context.Context) string {
  100. if ud := GetUserDetails(ctx); ud != nil {
  101. return ud.ProductCode
  102. }
  103. return ""
  104. }