feat/create_new_token #2
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
package metric
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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{
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 convertMap(claims), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, domain.TokenClaimError("get data from claim map error")
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseToken(ctx context.Context, accessToken string, secret string) (jwt.MapClaims, error) {
|
||||||
|
token, err := jwt.Parse(accessToken, func(token *jwt.Token) (any, error) {
|
||||||
|
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
|
||||||
|
return nil, domain.TokenUnexpectedSigningErr(fmt.Sprintf("token unexpected signing method: %v", token.Header["alg"]))
|
||||||
|
}
|
||||||
|
|
||||||
|
return []byte(secret), nil
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return jwt.MapClaims{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
claims, ok := token.Claims.(jwt.MapClaims)
|
||||||
|
|
||||||
|
if !(ok && token.Valid) {
|
||||||
|
return jwt.MapClaims{}, domain.TokenTokenValidateErr("token valid error")
|
||||||
|
}
|
||||||
|
|
||||||
return claims, nil
|
return claims, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, fmt.Errorf("get data from claim map error")
|
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)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
func parseToken(accessToken string) (jwt.MapClaims, error) {
|
return output
|
||||||
// token, err := jwt.Parse(accessToken, func(token *jwt.Token) (interface{}, error) {
|
|
||||||
// if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
|
|
||||||
// return nil, domain.TokenUnexpectedSigningErr(fmt.Sprintf("token unexpected signing method: %v", token.Header["alg"]))
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// return []byte(uc.Config.CustomConfig.Token.Secret), nil
|
|
||||||
// })
|
|
||||||
//
|
|
||||||
// if err != nil {
|
|
||||||
// ers.FromCode()
|
|
||||||
// return jwt.MapClaims{}, usecase.TokenError{Msg: fmt.Sprintf("parse token error: %s token: %s", err.Error(), accessToken)}
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// claims, ok := token.Claims.(jwt.MapClaims)
|
|
||||||
//
|
|
||||||
// if !(ok && token.Valid) {
|
|
||||||
// return jwt.MapClaims{}, usecase.TokenError{Msg: "token valid error"}
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// return claims, nil
|
|
||||||
return nil, nil
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue