feat(auth): 添加设备绑定数量限制检查
All checks were successful
Build docker and publish / build (20.15.1) (push) Successful in 7m26s

在设备绑定逻辑中添加对设备绑定数量的限制检查,当超过限制时返回特定错误码
同时在用户注册、登录等流程中处理设备绑定数量超限的错误情况
This commit is contained in:
shanshanzhong 2025-11-27 23:58:10 -08:00
parent 2442831cd7
commit 6afd6eb307
7 changed files with 90 additions and 58 deletions

View File

@ -88,6 +88,14 @@ func (l *BindDeviceLogic) createDeviceForUser(identifier, ip, userAgent string,
logger.Field("user_id", userId),
)
// enforce device bind limit before creating
if limit := l.svcCtx.SessionLimit(); limit > 0 {
if _, count, err := l.svcCtx.UserModel.QueryDeviceList(l.ctx, userId); err == nil {
if count >= limit {
return xerr.NewErrCodeMsg(xerr.DeviceBindLimitExceeded, "账户绑定设备数已达上限,请移除其他设备后再登录,您也可以再注册一个新账户使用,点击帮助中心查看更多详情。")
}
}
}
err := l.svcCtx.UserModel.Transaction(l.ctx, func(db *gorm.DB) error {
// Create device auth method
authMethod := &user.AuthMethods{
@ -146,6 +154,14 @@ func (l *BindDeviceLogic) createDeviceForUser(identifier, ip, userAgent string,
func (l *BindDeviceLogic) rebindDeviceToNewUser(deviceInfo *user.Device, ip, userAgent string, newUserId int64) error {
oldUserId := deviceInfo.UserId
// enforce device bind limit before rebind
if limit := l.svcCtx.SessionLimit(); limit > 0 {
if _, count, err := l.svcCtx.UserModel.QueryDeviceList(l.ctx, newUserId); err == nil {
if count >= limit {
return xerr.NewErrCodeMsg(xerr.DeviceBindLimitExceeded, "账户绑定设备数已达上限,请移除其他设备后再登录,您也可以再注册一个新账户使用,点击帮助中心查看更多详情。")
}
}
}
err := l.svcCtx.UserModel.Transaction(l.ctx, func(db *gorm.DB) error {
// Check if old user has other auth methods besides device
var authMethods []user.AuthMethods

View File

@ -113,12 +113,15 @@ func (l *ResetPasswordLogic) ResetPassword(req *types.ResetPasswordRequest) (res
if req.Identifier != "" {
bindLogic := NewBindDeviceLogic(l.ctx, l.svcCtx)
if err := bindLogic.BindDeviceToUser(req.Identifier, req.IP, req.UserAgent, userInfo.Id); err != nil {
var ce *xerr.CodeError
if errors.As(err, &ce) && ce.GetErrCode() == xerr.DeviceBindLimitExceeded {
return nil, ce
}
l.Errorw("failed to bind device to user",
logger.Field("user_id", userInfo.Id),
logger.Field("identifier", req.Identifier),
logger.Field("error", err.Error()),
)
// Don't fail register if device binding fails, just log the error
}
}
if l.ctx.Value(constant.LoginType) != nil {

View File

@ -128,12 +128,15 @@ func (l *TelephoneLoginLogic) TelephoneLogin(req *types.TelephoneLoginRequest, r
if req.Identifier != "" {
bindLogic := NewBindDeviceLogic(l.ctx, l.svcCtx)
if err := bindLogic.BindDeviceToUser(req.Identifier, req.IP, req.UserAgent, userInfo.Id); err != nil {
var ce *xerr.CodeError
if errors.As(err, &ce) && ce.GetErrCode() == xerr.DeviceBindLimitExceeded {
return nil, ce
}
l.Errorw("failed to bind device to user",
logger.Field("user_id", userInfo.Id),
logger.Field("identifier", req.Identifier),
logger.Field("error", err.Error()),
)
// Don't fail login if device binding fails, just log the error
}
}

View File

@ -144,12 +144,15 @@ func (l *TelephoneUserRegisterLogic) TelephoneUserRegister(req *types.TelephoneR
if req.Identifier != "" {
bindLogic := NewBindDeviceLogic(l.ctx, l.svcCtx)
if err := bindLogic.BindDeviceToUser(req.Identifier, req.IP, req.UserAgent, userInfo.Id); err != nil {
var ce *xerr.CodeError
if errors.As(err, &ce) && ce.GetErrCode() == xerr.DeviceBindLimitExceeded {
return nil, ce
}
l.Errorw("failed to bind device to user",
logger.Field("user_id", userInfo.Id),
logger.Field("identifier", req.Identifier),
logger.Field("error", err.Error()),
)
// Don't fail register if device binding fails, just log the error
}
}
if l.ctx.Value(constant.LoginType) != nil {

View File

@ -85,12 +85,15 @@ func (l *UserLoginLogic) UserLogin(req *types.UserLoginRequest) (resp *types.Log
if req.Identifier != "" {
bindLogic := NewBindDeviceLogic(l.ctx, l.svcCtx)
if err := bindLogic.BindDeviceToUser(req.Identifier, req.IP, req.UserAgent, userInfo.Id); err != nil {
var ce *xerr.CodeError
if errors.As(err, &ce) && ce.GetErrCode() == xerr.DeviceBindLimitExceeded {
return nil, ce
}
l.Errorw("failed to bind device to user",
logger.Field("user_id", userInfo.Id),
logger.Field("identifier", req.Identifier),
logger.Field("error", err.Error()),
)
// Don't fail login if device binding fails, just log the error
}
}
if l.ctx.Value(constant.LoginType) != nil {

View File

@ -130,12 +130,15 @@ func (l *UserRegisterLogic) UserRegister(req *types.UserRegisterRequest) (resp *
if req.Identifier != "" {
bindLogic := NewBindDeviceLogic(l.ctx, l.svcCtx)
if err := bindLogic.BindDeviceToUser(req.Identifier, req.IP, req.UserAgent, userInfo.Id); err != nil {
var ce *xerr.CodeError
if errors.As(err, &ce) && ce.GetErrCode() == xerr.DeviceBindLimitExceeded {
return nil, ce
}
l.Errorw("failed to bind device to user",
logger.Field("user_id", userInfo.Id),
logger.Field("identifier", req.Identifier),
logger.Field("error", err.Error()),
)
// Don't fail register if device binding fails, just log the error
}
}
if l.ctx.Value(constant.LoginType) != nil {

View File

@ -116,6 +116,7 @@ const (
const (
DeviceNotExist uint32 = 90017
UseridNotMatch uint32 = 90018
DeviceBindLimitExceeded uint32 = 90019
)
const (