package tokenservicelogic import ( "app-cloudep-permission-server/internal/config" "app-cloudep-permission-server/internal/domain" "app-cloudep-permission-server/internal/entity" ers "code.30cm.net/digimon/library-go/errors" "context" "github.com/google/uuid" "time" "app-cloudep-permission-server/gen_result/pb/permission" "app-cloudep-permission-server/internal/svc" "github.com/zeromicro/go-zero/core/logx" ) type NewTokenLogic struct { ctx context.Context svcCtx *svc.ServiceContext logx.Logger } func NewNewTokenLogic(ctx context.Context, svcCtx *svc.ServiceContext) *NewTokenLogic { return &NewTokenLogic{ ctx: ctx, svcCtx: svcCtx, Logger: logx.WithContext(ctx), } } // https://datatracker.ietf.org/doc/html/rfc6749#section-3.3 type authorizationReq struct { GrantType domain.GrantType `json:"grant_type" validate:"required,oneof=password client_credentials refresh_token"` DeviceID string `json:"device_id"` Scope string `json:"scope" validate:"required"` Data map[string]string `json:"data"` Expires int `json:"expires"` IsRefreshToken bool `json:"is_refresh_token"` } // NewToken 建立一個新的 Token,例如:AccessToken func (l *NewTokenLogic) NewToken(in *permission.AuthorizationReq) (*permission.TokenResp, error) { data := authorizationReq{ GrantType: domain.GrantType(in.GetGrantType()), Scope: in.GetScope(), DeviceID: in.GetDeviceId(), Data: in.GetData(), Expires: int(in.GetExpires()), IsRefreshToken: in.GetIsRefreshToken(), } // 驗證所需 if err := l.svcCtx.Validate.ValidateAll(&data); err != nil { return nil, ers.InvalidFormat(err.Error()) } token, err := newToken(data, l.svcCtx.Config) if err != nil { return nil, err } err = l.svcCtx.TokenRedisRepo.Create(l.ctx, *token) if err != nil { logx.WithCallerSkip(1).WithFields( logx.Field("func", "TokenRedisRepo.Create"), logx.Field("token", token), ).Error(err.Error()) return nil, err } return &permission.TokenResp{ AccessToken: token.AccessToken, TokenType: domain.TokenTypeBearer, ExpiresIn: int32(token.ExpiresIn), RefreshToken: token.RefreshToken, }, nil } func newToken(authReq authorizationReq, cfg config.Config) (*entity.Token, error) { // 準備建立 Token 所需 now := time.Now().UTC() expires := authReq.Expires refreshExpires := authReq.Expires if expires <= 0 { // 將時間加上 300 秒 sec := time.Duration(cfg.Token.Expired.Seconds()) * time.Second newTime := now.Add(sec) // 獲取 Unix 時間戳 timestamp := newTime.Unix() expires = int(timestamp) refreshExpires = expires } // 如果這是一個 Refresh Token 過期時間要比普通的Token 長 if authReq.IsRefreshToken { // 將時間加上 300 秒 sec := time.Duration(cfg.Token.RefreshExpires.Seconds()) * time.Second newTime := now.Add(sec) // 獲取 Unix 時間戳 timestamp := newTime.Unix() refreshExpires = int(timestamp) } token := entity.Token{ ID: uuid.Must(uuid.NewRandom()).String(), DeviceID: authReq.DeviceID, ExpiresIn: expires, RefreshExpiresIn: refreshExpires, AccessCreateAt: now, RefreshCreateAt: now, } claims := claims(authReq.Data) claims.SetRole(domain.DefaultRole) claims.SetID(token.ID) claims.SetScope(authReq.Scope) token.UID = claims.UID() if authReq.DeviceID != "" { claims.SetDeviceID(authReq.DeviceID) } var err error token.AccessToken, err = generateAccessTokenFunc(token, claims, cfg.Token.Secret) if err != nil { logx.WithCallerSkip(1).WithFields( logx.Field("func", "generateAccessTokenFunc"), logx.Field("claims", claims), ).Error(err.Error()) return nil, err } if authReq.IsRefreshToken { token.RefreshToken = generateRefreshTokenFunc(token.AccessToken) } return &token, nil }