feat/create_new_token #2

Merged
daniel.w merged 10 commits from feat/create_new_token into main 2024-08-12 14:20:15 +00:00
8 changed files with 170 additions and 81 deletions
Showing only changes of commit b111cc1210 - Show all commits

View File

@ -1,20 +1,57 @@
package domain package domain
import ( import (
mts "ark-permission/internal/lib/metric"
ers "code.30cm.net/wanderland/library-go/errors" ers "code.30cm.net/wanderland/library-go/errors"
"code.30cm.net/wanderland/library-go/errors/code" "code.30cm.net/wanderland/library-go/errors/code"
) )
// Decimal: 120314
// 12 represents Scope // 12 represents Scope
// 03 represents Category // 100 represents Category
// 14 represents Detail error code // 9 represents Detail error code
// full code 12009 只會有 系統以及錯誤碼category 是給系統判定用的
// 目前 Scope 以及分類要系統共用,係向的錯誤各自服務實作就好
const ( const (
TokenUnexpectedSigning = 1 TokenUnexpectedSigningErrorCode = iota + 1
TokenValidateErrorCode
TokenClaimErrorCode
) )
// TokenUnexpectedSigningErr 031011 const (
RedisDelErrorCode = iota + 20
RedisPipLineErrorCode
)
// TokenUnexpectedSigningErr 30001 Token 簽名錯誤
func TokenUnexpectedSigningErr(msg string) *ers.Err { func TokenUnexpectedSigningErr(msg string) *ers.Err {
return ers.NewErr(code.CloudEPPermission, code.CatInput, code.InvalidFormat, msg) mts.AppErrorMetrics.AddFailure("token", "token_unexpected_sign")
return ers.NewErr(code.CloudEPPermission, code.CatInput, TokenUnexpectedSigningErrorCode, msg)
}
// TokenTokenValidateErr 30002 Token 驗證錯誤
func TokenTokenValidateErr(msg string) *ers.Err {
mts.AppErrorMetrics.AddFailure("token", "token_validate_ilegal")
return ers.NewErr(code.CloudEPPermission, code.CatInput, TokenValidateErrorCode, msg)
}
// TokenClaimError 30003 Token 驗證錯誤
func TokenClaimError(msg string) *ers.Err {
mts.AppErrorMetrics.AddFailure("token", "token_claim_error")
return ers.NewErr(code.CloudEPPermission, code.CatInput, TokenClaimErrorCode, msg)
}
// RedisDelError 30020 Redis 刪除錯誤
func RedisDelError(msg string) *ers.Err {
// 看需要建立哪些 Metrics
mts.AppErrorMetrics.AddFailure("redis", "del_error")
return ers.NewErr(code.CloudEPPermission, code.CatDB, RedisDelErrorCode, msg)
}
// RedisPipLineError 30021 Redis PipLine 錯誤
func RedisPipLineError(msg string) *ers.Err {
// 看需要建立哪些 Metrics
mts.AppErrorMetrics.AddFailure("redis", "pip_line_error")
return ers.NewErr(code.CloudEPPermission, code.CatInput, TokenClaimErrorCode, msg)
} }

View File

@ -0,0 +1,30 @@
package metric
import (
"github.com/zeromicro/go-zero/core/metric"
)
var AppErrorMetrics = NewAppErrMetrics()
type appErrMetrics struct {
metric.CounterVec
}
type Metrics interface {
AddFailure(source, reason string)
}
// NewAppErrMetrics initiate metrics and register to prometheus
func NewAppErrMetrics() Metrics {
return &appErrMetrics{metric.NewCounterVec(&metric.CounterVecOpts{
Namespace: "ark",
Subsystem: "permission",
Name: "permission_app_error_total",
Help: "App defined failure total.",
Labels: []string{"source", "reason"},
})}
}
func (m *appErrMetrics) AddFailure(source, reason string) {
m.Inc(source, reason)
}

View File

@ -0,0 +1 @@
package metric

View File

@ -3,6 +3,7 @@ package logic
import ( import (
"ark-permission/gen_result/pb/permission" "ark-permission/gen_result/pb/permission"
"ark-permission/internal/svc" "ark-permission/internal/svc"
ers "code.30cm.net/wanderland/library-go/errors"
"context" "context"
"github.com/zeromicro/go-zero/core/logx" "github.com/zeromicro/go-zero/core/logx"
) )
@ -27,34 +28,38 @@ type cancelTokenReq struct {
// CancelToken 取消 Token也包含他裡面的 One Time Toke // CancelToken 取消 Token也包含他裡面的 One Time Toke
func (l *CancelTokenLogic) CancelToken(in *permission.CancelTokenReq) (*permission.OKResp, error) { func (l *CancelTokenLogic) CancelToken(in *permission.CancelTokenReq) (*permission.OKResp, error) {
// // 驗證所需 // 驗證所需
// if err := l.svcCtx.Validate.ValidateAll(&cancelTokenReq{ if err := l.svcCtx.Validate.ValidateAll(&cancelTokenReq{
// Token: in.GetToken(), Token: in.GetToken(),
// }); err != nil { }); err != nil {
// return nil, ers.InvalidFormat(err.Error()) return nil, ers.InvalidFormat(err.Error())
// } }
// claims, err := uc.parseClaims(accessToken) claims, err := parseClaims(l.ctx, in.GetToken(), l.svcCtx.Config.Token.Secret)
// if err != nil { if err != nil {
// return err logx.WithCallerSkip(1).WithFields(
// } logx.Field("func", "parseClaims"),
// ).Error(err.Error())
// token, err := uc.TokenRepository.GetByAccess(ctx, claims.ID()) return nil, err
// if err != nil { }
// if errors.Is(err, repository.ErrRecordNotFound) {
// return usecase.TokenError{Msg: "token not found"} token, err := l.svcCtx.TokenRedisRepo.GetByAccess(l.ctx, claims.ID())
// } if err != nil {
// logx.WithCallerSkip(1).WithFields(
// return usecase.InternalError{Err: fmt.Errorf("tokenRepository.GetByAccess error: %w", err)} logx.Field("func", "TokenRedisRepo.GetByAccess"),
// } logx.Field("claims", claims),
// ).Error(err.Error())
// if err := uc.TokenRepository.Delete(ctx, token); err != nil { return nil, err
// if errors.Is(err, repository.ErrRecordNotFound) { }
// return nil, usecase.TokenError{Msg: "token not found"}
// } err = l.svcCtx.TokenRedisRepo.Delete(l.ctx, token)
// if err != nil {
// return nil, err logx.WithCallerSkip(1).WithFields(
// } logx.Field("func", "TokenRedisRepo.Delete"),
logx.Field("req", token),
).Error(err.Error())
return nil, err
}
return &permission.OKResp{}, nil return &permission.OKResp{}, nil
} }

View File

@ -7,7 +7,6 @@ import (
"ark-permission/internal/svc" "ark-permission/internal/svc"
ers "code.30cm.net/wanderland/library-go/errors" ers "code.30cm.net/wanderland/library-go/errors"
"context" "context"
"fmt"
"github.com/google/uuid" "github.com/google/uuid"
"time" "time"
@ -88,7 +87,11 @@ func (l *NewTokenLogic) NewToken(in *permission.AuthorizationReq) (*permission.T
var err error var err error
token.AccessToken, err = generateAccessTokenFunc(token, claims, l.svcCtx.Config.Token.Secret) token.AccessToken, err = generateAccessTokenFunc(token, claims, l.svcCtx.Config.Token.Secret)
if err != nil { if err != nil {
return nil, ers.ArkInternal(fmt.Errorf("accessGenerate token error: %w", err).Error()) logx.WithCallerSkip(1).WithFields(
logx.Field("func", "generateAccessTokenFunc"),
logx.Field("claims", claims),
).Error(err.Error())
return nil, err
} }
if in.GetIsRefreshToken() { if in.GetIsRefreshToken() {
@ -97,7 +100,11 @@ func (l *NewTokenLogic) NewToken(in *permission.AuthorizationReq) (*permission.T
err = l.svcCtx.TokenRedisRepo.Create(l.ctx, token) err = l.svcCtx.TokenRedisRepo.Create(l.ctx, token)
if err != nil { if err != nil {
return nil, ers.ArkInternal(fmt.Errorf("tokenRepository.Create error: %w", err).Error()) logx.WithCallerSkip(1).WithFields(
logx.Field("func", "TokenRedisRepo.Create"),
logx.Field("token", token),
).Error(err.Error())
return nil, err
} }
return &permission.TokenResp{ return &permission.TokenResp{

View File

@ -1,13 +1,9 @@
package logic package logic
import ( import (
"ark-permission/internal/domain"
"context"
"fmt"
"strconv"
"ark-permission/gen_result/pb/permission" "ark-permission/gen_result/pb/permission"
"ark-permission/internal/svc" "ark-permission/internal/svc"
"context"
"github.com/zeromicro/go-zero/core/logx" "github.com/zeromicro/go-zero/core/logx"
) )
@ -29,8 +25,6 @@ func NewRefreshTokenLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Refr
// RefreshToken 更新目前的token 以及裡面包含的一次性 Token // RefreshToken 更新目前的token 以及裡面包含的一次性 Token
func (l *RefreshTokenLogic) RefreshToken(in *permission.RefreshTokenReq) (*permission.RefreshTokenResp, error) { func (l *RefreshTokenLogic) RefreshToken(in *permission.RefreshTokenReq) (*permission.RefreshTokenResp, error) {
// todo: add your logic here and delete this line // todo: add your logic here and delete this line
e := domain.TokenUnexpectedSigningErr("gg88g88")
fmt.Printf(strconv.Itoa(int(e.Code())), e.Category(), e.Scope(), e.FullCode(), e.Error())
return &permission.RefreshTokenResp{}, nil return &permission.RefreshTokenResp{}, nil
} }

View File

@ -1,8 +1,10 @@
package logic package logic
import ( import (
"ark-permission/internal/domain"
"ark-permission/internal/entity" "ark-permission/internal/entity"
"bytes" "bytes"
"context"
"crypto/sha256" "crypto/sha256"
"encoding/hex" "encoding/hex"
"fmt" "fmt"
@ -23,7 +25,7 @@ func generateAccessToken(token entity.Token, data any, sign string) (string, err
accessToken, err := jwt.NewWithClaims(jwt.SigningMethodHS256, claim). accessToken, err := jwt.NewWithClaims(jwt.SigningMethodHS256, claim).
SignedString([]byte(sign)) SignedString([]byte(sign))
if err != nil { if err != nil {
return "", err return "", domain.TokenClaimError(err.Error())
} }
return accessToken, nil return accessToken, nil
@ -37,40 +39,54 @@ func generateRefreshToken(accessToken string) string {
return hex.EncodeToString(h.Sum(nil)) return hex.EncodeToString(h.Sum(nil))
} }
func parseClaims(accessToken string) (claims, error) { func parseClaims(ctx context.Context, accessToken string, secret string) (claims, error) {
claimMap, err := parseToken(accessToken) claimMap, err := parseToken(ctx, accessToken, secret)
if err != nil { if err != nil {
return claims{}, err return claims{}, err
} }
claims, ok := claimMap["data"].(map[string]string) claims, ok := claimMap["data"].(map[string]any)
if ok { if ok {
return claims, nil
return convertMap(claims), nil
} }
return nil, fmt.Errorf("get data from claim map error") return nil, domain.TokenClaimError("get data from claim map error")
} }
func parseToken(accessToken string) (jwt.MapClaims, error) { func parseToken(ctx context.Context, accessToken string, secret string) (jwt.MapClaims, error) {
// token, err := jwt.Parse(accessToken, func(token *jwt.Token) (interface{}, error) { token, err := jwt.Parse(accessToken, func(token *jwt.Token) (any, error) {
// if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
// return nil, domain.TokenUnexpectedSigningErr(fmt.Sprintf("token unexpected signing method: %v", token.Header["alg"])) return nil, domain.TokenUnexpectedSigningErr(fmt.Sprintf("token unexpected signing method: %v", token.Header["alg"]))
// } }
//
// return []byte(uc.Config.CustomConfig.Token.Secret), nil return []byte(secret), nil
// }) })
//
// if err != nil { if err != nil {
// ers.FromCode() return jwt.MapClaims{}, err
// return jwt.MapClaims{}, usecase.TokenError{Msg: fmt.Sprintf("parse token error: %s token: %s", err.Error(), accessToken)} }
// }
// claims, ok := token.Claims.(jwt.MapClaims)
// claims, ok := token.Claims.(jwt.MapClaims)
// if !(ok && token.Valid) {
// if !(ok && token.Valid) { return jwt.MapClaims{}, domain.TokenTokenValidateErr("token valid error")
// return jwt.MapClaims{}, usecase.TokenError{Msg: "token valid error"} }
// }
// return claims, nil
// return claims, nil }
return nil, nil
func convertMap(input map[string]interface{}) map[string]string {
output := make(map[string]string)
for key, value := range input {
switch v := value.(type) {
case string:
output[key] = v
case fmt.Stringer:
output[key] = v.String()
default:
output[key] = fmt.Sprintf("%v", value)
}
}
return output
} }

View File

@ -5,7 +5,6 @@ import (
"ark-permission/internal/domain/repository" "ark-permission/internal/domain/repository"
"ark-permission/internal/entity" "ark-permission/internal/entity"
ers "code.30cm.net/wanderland/library-go/errors" ers "code.30cm.net/wanderland/library-go/errors"
"context" "context"
"encoding/json" "encoding/json"
"errors" "errors"
@ -32,7 +31,7 @@ func NewTokenRepository(param TokenRepositoryParam) repository.TokenRepository {
func (t *tokenRepository) Create(ctx context.Context, token entity.Token) error { func (t *tokenRepository) Create(ctx context.Context, token entity.Token) error {
body, err := json.Marshal(token) body, err := json.Marshal(token)
if err != nil { if err != nil {
return wrapError("json.Marshal token error", err) return ers.ArkInternal("json.Marshal token error", err.Error())
} }
err = t.store.Pipelined(func(tx redis.Pipeliner) error { err = t.store.Pipelined(func(tx redis.Pipeliner) error {
@ -53,11 +52,11 @@ func (t *tokenRepository) Create(ctx context.Context, token entity.Token) error
return nil return nil
}) })
if err != nil { if err != nil {
return wrapError("store.Pipelined error", err) return domain.RedisPipLineError(err.Error())
} }
if err := t.SetUIDToken(token); err != nil { if err := t.SetUIDToken(token); err != nil {
return wrapError("SetUIDToken error", err) return ers.ArkInternal("SetUIDToken error", err.Error())
} }
return nil return nil
@ -76,7 +75,7 @@ func (t *tokenRepository) Delete(ctx context.Context, token entity.Token) error
for _, key := range keys { for _, key := range keys {
if err := tx.Del(ctx, key).Err(); err != nil { if err := tx.Del(ctx, key).Err(); err != nil {
return fmt.Errorf("store.Del key error: %w", err) return domain.RedisDelError(fmt.Sprintf("store.Del key error: %v", err))
} }
} }
@ -84,7 +83,7 @@ func (t *tokenRepository) Delete(ctx context.Context, token entity.Token) error
key := domain.DeviceTokenRedisKey.With(token.UID).ToString() key := domain.DeviceTokenRedisKey.With(token.UID).ToString()
_, err := t.store.Hdel(key, token.DeviceID) _, err := t.store.Hdel(key, token.DeviceID)
if err != nil { if err != nil {
return fmt.Errorf("store.HDel deviceKey error: %w", err) return domain.RedisDelError(fmt.Sprintf("store.HDel deviceKey error: %v", err))
} }
} }
@ -92,7 +91,7 @@ func (t *tokenRepository) Delete(ctx context.Context, token entity.Token) error
}) })
if err != nil { if err != nil {
return fmt.Errorf("store.Pipelined error: %w", err) return domain.RedisPipLineError(fmt.Sprintf("store.Pipelined error: %v", err))
} }
return nil return nil
@ -100,17 +99,17 @@ func (t *tokenRepository) Delete(ctx context.Context, token entity.Token) error
func (t *tokenRepository) get(key string) (entity.Token, error) { func (t *tokenRepository) get(key string) (entity.Token, error) {
body, err := t.store.Get(key) body, err := t.store.Get(key)
if errors.Is(err, redis.Nil) { if errors.Is(err, redis.Nil) || body == "" {
return entity.Token{}, ers.ResourceNotFound("token key not found in redis", key) return entity.Token{}, ers.ResourceNotFound("token key not found in redis", key)
} }
if err != nil { if err != nil {
return entity.Token{}, fmt.Errorf("store.Get tokenTag error: %w", err) return entity.Token{}, ers.ArkInternal(fmt.Sprintf("store.Get tokenTag error: %v", err))
} }
var token entity.Token var token entity.Token
if err := json.Unmarshal([]byte(body), &token); err != nil { if err := json.Unmarshal([]byte(body), &token); err != nil {
return entity.Token{}, fmt.Errorf("json.Unmarshal token error: %w", err) return entity.Token{}, ers.ArkInternal(fmt.Sprintf("json.Unmarshal token error: %w", err))
} }
return token, nil return token, nil