1
This commit is contained in:
+1
-1
@@ -67,7 +67,7 @@ Flutter App ── POST /auth/register|login ─► 账号注册/登录,签发
|
||||
|
||||
### POST /api/v1/auth/login
|
||||
|
||||
手机号登录。请求:`{"phone": "13800000000", "password": "..."}`。响应 `data: {"token": "..."}`(HMAC 自签名,有效期 `auth.token_ttl`,默认 30 天)。后续登录态接口携带 `Authorization: Bearer <token>`。
|
||||
手机号登录(自动注册式:手机号不存在则以输入密码创建账号,存在但密码为空则补写输入密码,仅已有密码时才做密码比对,比对失败报错;创建/补密码后即登录成功)。请求:`{"phone": "13800000000", "password": "..."}`。响应 `data: {"token": "..."}`(HMAC 自签名,有效期 `auth.token_ttl`,默认 30 天)。后续登录态接口携带 `Authorization: Bearer <token>`。
|
||||
|
||||
### GET /api/v1/plans
|
||||
|
||||
|
||||
@@ -45,9 +45,39 @@ func TestAuthRegisterLogin(t *testing.T) {
|
||||
if _, err := Auth.Register(ctx(), &dto.RegisterReq{Phone: phone, Password: "other123"}); err == nil {
|
||||
t.Fatal("duplicate register should fail")
|
||||
}
|
||||
// 未注册手机号登录失败
|
||||
if _, err := Auth.Login(ctx(), &dto.LoginReq{Phone: uniquePhone(), Password: "pass123456"}); err == nil {
|
||||
t.Fatal("login unregistered phone should fail")
|
||||
// 未注册手机号登录自动创建账号并登录成功(自动注册式登录)
|
||||
phone2 := uniquePhone()
|
||||
res2, err := Auth.Login(ctx(), &dto.LoginReq{Phone: phone2, Password: "pass123456"})
|
||||
if err != nil {
|
||||
t.Fatalf("login auto-register: %v", err)
|
||||
}
|
||||
got2, err := common.ParseToken(ctx(), res2.Token)
|
||||
if err != nil || got2 != phone2 {
|
||||
t.Fatalf("auto-register token = %q, %v; want %q", got2, err, phone2)
|
||||
}
|
||||
// 自动注册后已存在,重复注册报错
|
||||
if _, err := Auth.Register(ctx(), &dto.RegisterReq{Phone: phone2, Password: "other123"}); err == nil {
|
||||
t.Fatal("register after auto-register should fail")
|
||||
}
|
||||
}
|
||||
|
||||
// TestAuthLoginFillPlaceholder 运营发卡占位行(无密码)→ 登录用输入密码补写并成功,授权保留;错误密码被拒
|
||||
func TestAuthLoginFillPlaceholder(t *testing.T) {
|
||||
phone := uniquePhone()
|
||||
if _, err := License.AdminGrant(ctx(), &dto.AdminGrantReq{PhoneNum: phone, PlanId: "day"}); err != nil {
|
||||
t.Fatalf("grant: %v", err)
|
||||
}
|
||||
if _, err := Auth.Login(ctx(), &dto.LoginReq{Phone: phone, Password: "pass123456"}); err != nil {
|
||||
t.Fatalf("login fill password: %v", err)
|
||||
}
|
||||
// 补密码不能清掉已授权
|
||||
lic, err := dao.License.GetByPhone(ctx(), phone)
|
||||
if err != nil || lic == nil || lic.ExpiresAt == nil {
|
||||
t.Fatalf("grant lost after login fill password: %+v, %v", lic, err)
|
||||
}
|
||||
// 密码已写入:错误密码被拒
|
||||
if _, err := Auth.Login(ctx(), &dto.LoginReq{Phone: phone, Password: "wrong"}); err == nil {
|
||||
t.Fatal("wrong password should fail after fill")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -46,12 +46,44 @@ func (s *authService) Register(ctx context.Context, req *dto.RegisterReq) (*dto.
|
||||
return &dto.RegisterRes{}, nil
|
||||
}
|
||||
|
||||
// Login 登录:绕缓存查账号(密码变更即时生效)→ bcrypt 比对 → 签发 token
|
||||
// Login 登录(自动注册式):绕缓存查账号(密码变更即时生效)。
|
||||
// 手机号不存在 → 以输入密码创建账号;存在但密码为空(运营发卡占位行)→ 补写输入密码;
|
||||
// 仅已有密码才做 bcrypt 比对,失败报错。创建/补密码后即视为登录成功签发 token。
|
||||
func (s *authService) Login(ctx context.Context, req *dto.LoginReq) (*dto.LoginRes, error) {
|
||||
lic, err := dao.License.GetByPhoneNoCache(ctx, req.Phone)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if lic == nil || lic.Password == "" {
|
||||
hash, err := bcrypt.GenerateFromPassword([]byte(req.Password), bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
return nil, gerror.Wrap(err, "密码加密失败")
|
||||
}
|
||||
err = common.Serial().Submit(ctx, func() error {
|
||||
return g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
|
||||
cur, err := dao.License.GetByPhoneInTx(ctx, tx, req.Phone)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if cur == nil {
|
||||
return dao.License.Register(ctx, tx, req.Phone, string(hash))
|
||||
}
|
||||
if cur.Password == "" {
|
||||
return dao.License.SetPasswordInTx(ctx, tx, req.Phone, string(hash))
|
||||
}
|
||||
return nil
|
||||
})
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
common.ClearCache(ctx, "license:"+req.Phone)
|
||||
// 并发下密码可能已被他人补写,重新读取后统一走比对
|
||||
lic, err = dao.License.GetByPhoneNoCache(ctx, req.Phone)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if lic == nil || lic.Password == "" {
|
||||
return nil, gerror.New("账号或密码错误")
|
||||
}
|
||||
|
||||
BIN
Binary file not shown.
Reference in New Issue
Block a user