github.com/SupenBysz/gf-admin-community@v0.7.4/internal/logic/sys_jwt/sys_jwt.go (about)

     1  package sys_jwt
     2  
     3  import (
     4  	"context"
     5  	"github.com/SupenBysz/gf-admin-community/sys_model"
     6  	"github.com/SupenBysz/gf-admin-community/sys_model/sys_enum"
     7  	"github.com/SupenBysz/gf-admin-community/sys_model/sys_hook"
     8  	"github.com/SupenBysz/gf-admin-community/sys_service"
     9  	"github.com/SupenBysz/gf-admin-community/utility/response"
    10  	"github.com/yitter/idgenerator-go/idgen"
    11  	"time"
    12  
    13  	"github.com/gogf/gf/v2/errors/gerror"
    14  	"github.com/gogf/gf/v2/frame/g"
    15  	"github.com/gogf/gf/v2/net/ghttp"
    16  	"github.com/gogf/gf/v2/os/gctx"
    17  	"github.com/gogf/gf/v2/text/gstr"
    18  	"github.com/golang-jwt/jwt/v4"
    19  	"golang.org/x/sync/singleflight"
    20  )
    21  
    22  type hookInfo sys_model.KeyValueT[int64, sys_hook.JwtHookInfo]
    23  
    24  type sJwt struct {
    25  	SigningKey []byte
    26  	hookArr    []hookInfo
    27  }
    28  
    29  var (
    30  	ConcurrencyControl = &singleflight.Group{}
    31  )
    32  
    33  func init() {
    34  	sys_service.RegisterJwt(New())
    35  }
    36  
    37  // New MiddlewareJwt 权限控制
    38  func New() *sJwt {
    39  	return &sJwt{
    40  		SigningKey: []byte(g.Cfg().MustGet(gctx.New(), "service.tokenSignKey").String()),
    41  	}
    42  }
    43  
    44  // InstallHook 安装Hook
    45  func (s *sJwt) InstallHook(userType sys_enum.UserType, hookFunc sys_hook.JwtHookFunc) int64 {
    46  	item := hookInfo{Key: idgen.NextId(), Value: sys_hook.JwtHookInfo{Key: userType, Value: hookFunc}}
    47  
    48  	s.hookArr = append(s.hookArr, item)
    49  	return item.Key
    50  }
    51  
    52  // UnInstallHook 卸载Hook
    53  func (s *sJwt) UnInstallHook(savedHookId int64) {
    54  	newFuncArr := make([]hookInfo, 0)
    55  	for _, item := range s.hookArr {
    56  		if item.Key != savedHookId {
    57  			newFuncArr = append(newFuncArr, item)
    58  			continue
    59  		}
    60  	}
    61  	s.hookArr = newFuncArr
    62  }
    63  
    64  // CleanAllHook 清除所有Hook
    65  func (s *sJwt) CleanAllHook() {
    66  	s.hookArr = make([]hookInfo, 0)
    67  }
    68  
    69  // GenerateToken 创建一个token
    70  func (s *sJwt) GenerateToken(ctx context.Context, user *sys_model.SysUser) (response *sys_model.TokenInfo, err error) {
    71  	user.Password = ""
    72  
    73  	customClaims := &sys_model.JwtCustomClaims{
    74  		SysUser:      *user,
    75  		IsSuperAdmin: user.Type == sys_enum.User.Type.SuperAdmin.Code(),
    76  		IsAdmin:      user.Type == sys_enum.User.Type.Admin.Code(),
    77  
    78  		RegisteredClaims: jwt.RegisteredClaims{
    79  			NotBefore: jwt.NewNumericDate(time.Now()),
    80  			ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour * 24 * 7)),
    81  			IssuedAt:  jwt.NewNumericDate(time.Now()),
    82  			Issuer:    "免啦街",
    83  			Subject:   "筷满客",
    84  		},
    85  	}
    86  
    87  	g.Try(ctx, func(ctx context.Context) {
    88  		for _, hook := range s.hookArr {
    89  			if hook.Value.Key.Code()&user.Type == user.Type || (user.Type == 64 && hook.Value.Key.Code() == 32) {
    90  				customClaims, err = hook.Value.Value(ctx, customClaims)
    91  				if err != nil {
    92  					break
    93  				}
    94  			}
    95  		}
    96  	})
    97  
    98  	token, err := s.CreateToken(customClaims)
    99  
   100  	if err != nil {
   101  		return nil, gerror.New("创建登录令牌失败")
   102  	}
   103  
   104  	return &sys_model.TokenInfo{
   105  		Token:    token,
   106  		ExpireAt: customClaims.RegisteredClaims.ExpiresAt.Time,
   107  	}, nil
   108  }
   109  
   110  // CreateToken 创建一个token
   111  func (s *sJwt) CreateToken(claims *sys_model.JwtCustomClaims) (string, error) {
   112  	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
   113  	return token.SignedString(s.SigningKey)
   114  }
   115  
   116  // RefreshToken 刷新Token,并发安全
   117  func (s *sJwt) RefreshToken(oldToken string, claims *sys_model.JwtCustomClaims) (string, error) {
   118  	v, err, _ := ConcurrencyControl.Do("JWT:"+oldToken, func() (interface{}, error) {
   119  		return s.CreateToken(claims)
   120  	})
   121  	return v.(string), err
   122  }
   123  
   124  func (s *sJwt) Middleware(r *ghttp.Request) {
   125  	tokenString := gstr.Trim(r.Header.Get("Authorization"))
   126  
   127  	if gstr.ToUpper(r.Method) == "GET" && tokenString == "" {
   128  		tokenString = r.GetParam("token", "").String()
   129  	}
   130  
   131  	s.MakeSession(r.Context(), tokenString)
   132  	return
   133  }
   134  
   135  func (s *sJwt) MakeSession(ctx context.Context, tokenString string) *sys_model.JwtCustomClaims {
   136  	if gstr.HasPrefix(tokenString, "Bearer ") {
   137  		tokenString = gstr.SubStr(tokenString, 7)
   138  	}
   139  
   140  	token, err := jwt.ParseWithClaims(tokenString, &sys_model.JwtCustomClaims{}, func(token *jwt.Token) (i interface{}, e error) {
   141  		return s.SigningKey, nil
   142  	})
   143  
   144  	isCustomSession := sys_service.SysSession().HasCustom(ctx)
   145  
   146  	if err != nil {
   147  		if ve, ok := err.(*jwt.ValidationError); ok {
   148  			if ve.Errors&jwt.ValidationErrorMalformed != 0 {
   149  				err = gerror.New("无效TOKEN")
   150  			} else if ve.Errors&jwt.ValidationErrorExpired != 0 {
   151  				// Token is expired
   152  				err = gerror.New("TOKEN 已过期")
   153  			} else if ve.Errors&jwt.ValidationErrorNotValidYet != 0 {
   154  				err = gerror.New("TOKEN 未激活")
   155  			} else if ve.Errors&jwt.ValidationErrorSignatureInvalid != 0 {
   156  				err = gerror.New("TOKEN 签名无效")
   157  			} else {
   158  				err = gerror.New("解析TOKEN失败")
   159  			}
   160  		}
   161  		if !isCustomSession {
   162  			response.JsonExit(g.RequestFromCtx(ctx), 401, err.Error())
   163  		}
   164  		return nil
   165  	}
   166  
   167  	if token != nil {
   168  		if claims, ok := token.Claims.(*sys_model.JwtCustomClaims); ok && token.Valid {
   169  			if !isCustomSession {
   170  				sys_service.SysSession().SetUser(ctx, claims)
   171  				g.RequestFromCtx(ctx).Middleware.Next()
   172  			}
   173  			return claims
   174  		}
   175  	}
   176  
   177  	if !isCustomSession {
   178  		response.JsonExit(g.RequestFromCtx(ctx), 401, "解析TOKEN失败")
   179  	}
   180  	return nil
   181  }