package auth import ( "context" "encoding/base64" "errors" "github.com/goccy/go-json" "gofiber.study.skcks.cn/common/logger" "gofiber.study.skcks.cn/common/utils" "gofiber.study.skcks.cn/global" "gofiber.study.skcks.cn/middleware" "gofiber.study.skcks.cn/model/dto" "gofiber.study.skcks.cn/model/generic/models" "gofiber.study.skcks.cn/model/vo" "time" ) var ( PasswordIsEmpty = errors.New("密码不能为空") Failed = errors.New("账号或密码错误") InvalidRefreshToken = errors.New("刷新令牌已失效") ) const ( Separator = ":" RefreshTokenPrefix = "RefreshToken" + Separator ) type Service struct { } var Services *Service func InitService() { Services = &Service{} } // generateAndSaveRefreshToken // // 生成并保存 refreshToken 刷新令牌 func (s *Service) generateAndSaveRefreshToken(user *models.User) (refreshTokenB64 string, err error) { refreshTokenB64, err = global.GetNanoId() if err != nil { return } refreshTokenB64 = user.Id + Separator + refreshTokenB64 expire := time.Duration(global.Config.Jwt.Expire*2) * time.Second ctx := context.Background() global.Redis.Set(ctx, RefreshTokenPrefix+refreshTokenB64, utils.Json(user), expire) // base64 加密 return base64.StdEncoding.EncodeToString([]byte(refreshTokenB64)), err } func (s *Service) fromRefreshTokenGetUserCache(refreshTokenB64 string) (user *models.User, err error) { refreshTokenBytes, err := base64.StdEncoding.DecodeString(refreshTokenB64) if err != nil { return nil, InvalidRefreshToken } refreshToken := RefreshTokenPrefix + string(refreshTokenBytes) ctx := context.Background() data, err := global.Redis.Get(ctx, refreshToken).Result() if err != nil { return nil, InvalidRefreshToken } global.Redis.Del(ctx, refreshToken) cache := &models.User{} err = json.Unmarshal([]byte(data), cache) if err != nil { return nil, InvalidRefreshToken } user = &models.User{Id: cache.Id, Account: cache.Account} exist, err := global.DataSources.Get(user) if !exist { logger.Log.Infof("未能从 %s 找到用户信息", refreshToken) return nil, InvalidRefreshToken } return } func (s *Service) getUserClaims(user *models.User) global.UserClaims { return global.UserClaims{ Id: user.Id, Account: user.Account, Identify: "user" + middleware.CasBinSeparator + user.Account, } } func (s *Service) Login(login *dto.Login) (result *vo.Login, err error) { err = global.ValidateStruct(login) if err != nil { return nil, err } user := &models.User{Account: login.Account, Password: login.Password} exist, err := global.DataSources.Get(user) if err != nil { return } if !exist { return nil, Failed } token, err := global.GetToken(s.getUserClaims(user)) if err != nil { return } refreshToken, err := s.generateAndSaveRefreshToken(user) _, _ = global.DataSources.Update(user) return &vo.Login{ Token: token, RefreshToken: refreshToken, }, err } func (s *Service) RefreshToken(refreshTokenB64 string) (result *vo.Login, err error) { user, err := s.fromRefreshTokenGetUserCache(refreshTokenB64) if err != nil { return } token, err := global.GetToken(s.getUserClaims(user)) if err != nil { return } refreshToken, err := s.generateAndSaveRefreshToken(user) return &vo.Login{ Token: token, RefreshToken: refreshToken, }, err }