From 9c89f0fe4ad7b4ec17fd126e0d90dd860b358a75 Mon Sep 17 00:00:00 2001 From: eson <474420502@qq.com> Date: Mon, 4 Sep 2023 01:40:28 +0800 Subject: [PATCH] TODO: password --- .../logic/useremailconfirmationlogic.go | 45 +++++++++++-- server/auth/internal/types/types.go | 3 +- server_api/auth.api | 3 +- utils/auth/confirmation_link.go | 65 ++++++++++++++----- utils/auth/confirmation_link_test.go | 7 +- utils/auth/register.go | 1 + 6 files changed, 97 insertions(+), 27 deletions(-) diff --git a/server/auth/internal/logic/useremailconfirmationlogic.go b/server/auth/internal/logic/useremailconfirmationlogic.go index 351bdc5b..08050ee7 100644 --- a/server/auth/internal/logic/useremailconfirmationlogic.go +++ b/server/auth/internal/logic/useremailconfirmationlogic.go @@ -92,14 +92,15 @@ func (l *UserEmailConfirmationLogic) UserEmailConfirmation(req *types.RequestEma // 返回值必须调用Set重新返回, resp可以空指针调用 resp.SetStatus(basic.CodeOK, data) // userinfo 传入值时, 一定不为null - token, err := l.svcCtx.OAuthTokenManger.Decrypt(req.Token) - if err != nil { - logx.Error(err) - return resp.SetStatus(basic.CodeOAuthRegisterTokenErr) - } - - switch token.OperateType { + switch auth.OperateType(req.OpType) { case auth.OpTypeRegister: + + token, err := l.svcCtx.OAuthTokenManger.Decrypt(req.Token) + if err != nil { + logx.Error(err) + return resp.SetStatus(basic.CodeOAuthRegisterTokenErr) + } + if time.Since(token.CreateAt) > 30*time.Minute { return resp.SetStatusWithMessage(basic.CodeOAuthConfirmationTimeoutErr, "Verification links expire after 30 minute.") } @@ -135,6 +136,36 @@ func (l *UserEmailConfirmationLogic) UserEmailConfirmation(req *types.RequestEma } logx.Info("success:", token.TraceId) } + case auth.OpTypeResetToken: + + rt, err := l.svcCtx.ResetTokenManger.Decrypt(req.Token) // ResetToken + if err != nil { + logx.Error(err) + return resp.SetStatus(basic.CodeOAuthResetTokenDecryptErr, err.Error()) + } + + // TODO: 存储 + if rt.OperateType != auth.OpTypeResetToken { + return resp.SetStatus(basic.CodeOAuthTypeErr, "error OperateType: rt.OperateType != auth.OpTypeResetToken") + } + + err = l.svcCtx.AllModels.FsUser.Transaction(l.ctx, func(tx *gorm.DB) error { + user := &gmodel.FsUser{Id: int64(rt.UserId)} + err := tx.Take(user).Error + if err != nil { + return err + } + if *user.PasswordHash != rt.OldPassword { + return fmt.Errorf("password had beed updated") + } + return tx.Update("PasswordHash", rt.NewPassword).Error + }) + + if err != nil { + return resp.SetStatus(basic.CodeDbSqlErr, err.Error()) + } + + return resp.SetStatus(basic.CodeOK) default: return resp.SetStatus(basic.CodeOAuthRegisterTokenErr) diff --git a/server/auth/internal/types/types.go b/server/auth/internal/types/types.go index e7f6d228..4fda1d3d 100644 --- a/server/auth/internal/types/types.go +++ b/server/auth/internal/types/types.go @@ -48,7 +48,8 @@ type RequestGoogleLogin struct { } type RequestEmailConfirmation struct { - Token string `form:"token"` // 操作Token + Token string `form:"token"` // 操作Token + OpType string `form:"optype"` // 操作类型 } type RequestEmailRegister struct { diff --git a/server_api/auth.api b/server_api/auth.api index 59e07e45..3e0279a9 100644 --- a/server_api/auth.api +++ b/server_api/auth.api @@ -102,7 +102,8 @@ type RequestGoogleLogin { } type RequestEmailConfirmation { - Token string `form:"token"` // 操作Token + Token string `form:"token"` // 操作Token + OpType string `form:"optype"` // 操作类型 } type RequestEmailRegister { diff --git a/utils/auth/confirmation_link.go b/utils/auth/confirmation_link.go index 6b0115d8..4244919f 100644 --- a/utils/auth/confirmation_link.go +++ b/utils/auth/confirmation_link.go @@ -1,23 +1,27 @@ package auth import ( + "fmt" "fusenapi/utils/encryption_decryption" "net/url" + "reflect" ) -type OperateType int8 +type OperateType string const ( - OpTypeRegister OperateType = 1 //注册的操作类型 - OpTypeResetToken OperateType = 2 //重置密码类型 + OpTypeRegister OperateType = "1" //注册的操作类型 + OpTypeResetToken OperateType = "2" //重置密码类型 ) type ConfirmationLink[T any] struct { // Secret []byte - SecretGCM *encryption_decryption.SecretGCM[T] + secretGCM *encryption_decryption.SecretGCM[T] - DefaultQueryKey string // 默认key 是 token - link *url.URL + defaultTokenKey string // 默认key 是 token + defaultOpTypeKey string + + link *url.URL } func NewConfirmationLink[T any](key string, UrlStr string) *ConfirmationLink[T] { @@ -27,31 +31,58 @@ func NewConfirmationLink[T any](key string, UrlStr string) *ConfirmationLink[T] } return &ConfirmationLink[T]{ - SecretGCM: encryption_decryption.NewSecretGCM[T](key), - DefaultQueryKey: "token", - link: u, + secretGCM: encryption_decryption.NewSecretGCM[T](key), + defaultTokenKey: "token", + defaultOpTypeKey: "optype", + link: u, } } +// WithDefaultTokenKey 设置默认Token Key +func (cl *ConfirmationLink[T]) WithDefaultTokenKey(tkey string) *ConfirmationLink[T] { + cl.defaultTokenKey = tkey + return cl +} + +// WithDefaultOpTypeKey 设置默认OpType Key +func (cl *ConfirmationLink[T]) WithDefaultOpTypeKey(opkey string) *ConfirmationLink[T] { + cl.defaultTokenKey = opkey + return cl +} + // Generate 序列化链接传入需求的obj func (cl *ConfirmationLink[T]) Generate(obj *T) (string, error) { + vValue := reflect.ValueOf(obj).Elem() + opType := vValue.FieldByName("OperateType") + if !opType.IsValid() { + return "", fmt.Errorf("传入结构体 必须继承 OperateType") + } + + op := opType.Interface().(OperateType) + token, err := cl.Encrypt(obj) if err != nil { return "", err } - return cl.GenerateWithToken(token) + return cl.generateWithToken(token, op) } -// GenerateWithToken 序列化url带token -func (cl *ConfirmationLink[T]) GenerateWithToken(token string) (string, error) { +// generateWithToken 序列化url带token +func (cl *ConfirmationLink[T]) generateWithToken(token string, optype OperateType) (string, error) { q := cl.link.Query() - if q.Has(cl.DefaultQueryKey) { - q.Set(cl.DefaultQueryKey, token) + if q.Has(cl.defaultTokenKey) { + q.Set(cl.defaultTokenKey, token) } else { - q.Add(cl.DefaultQueryKey, token) + q.Add(cl.defaultTokenKey, token) + } + + if q.Has(cl.defaultOpTypeKey) { + q.Set(cl.defaultOpTypeKey, string(optype)) + } else { + q.Add(cl.defaultOpTypeKey, string(optype)) } // 生成确认链接 @@ -61,9 +92,9 @@ func (cl *ConfirmationLink[T]) GenerateWithToken(token string) (string, error) { } func (cl *ConfirmationLink[T]) Encrypt(obj *T) (string, error) { - return cl.SecretGCM.Encrypt(obj) + return cl.secretGCM.Encrypt(obj) } func (cl *ConfirmationLink[T]) Decrypt(ciphertext string) (*T, error) { - return cl.SecretGCM.Decrypt(ciphertext) + return cl.secretGCM.Decrypt(ciphertext) } diff --git a/utils/auth/confirmation_link_test.go b/utils/auth/confirmation_link_test.go index ecb708a3..ec4acfea 100644 --- a/utils/auth/confirmation_link_test.go +++ b/utils/auth/confirmation_link_test.go @@ -116,6 +116,7 @@ func BenchmarkAesXor(b *testing.B) { func TestConfirmationLink(t *testing.T) { type Register struct { + OperateType Id int64 Password string platform string @@ -125,12 +126,16 @@ func TestConfirmationLink(t *testing.T) { key := "21321321" cl := NewConfirmationLink[Register](key, "http://localhost:9900/api/auth/oauth2/register") - uri, _ := cl.Generate(&Register{Id: 39, Password: "21dsadsad", platform: "google", Expired: time.Now().UTC()}) + robj := &Register{Id: 39, Password: "21dsadsad", platform: "google", Expired: time.Now().UTC(), OperateType: OpTypeResetToken} + uri, _ := cl.Generate(robj) log.Println(uri) u, _ := url.Parse(uri) + log.Println(uri) + token := u.Query()["token"] log.Println(cl.Decrypt(token[0])) + } const secret = "your-256-bit-secret" diff --git a/utils/auth/register.go b/utils/auth/register.go index 3aacc85d..dec96fb2 100644 --- a/utils/auth/register.go +++ b/utils/auth/register.go @@ -42,6 +42,7 @@ type ResetToken struct { UserId int64 // guest_id 需要继承 Wid string // websocket 通道id Email string // email + NewPassword string // 新密码 OldPassword string // 旧密码 TraceId string //链路Id CreateAt time.Time // 创建时间