优化
This commit is contained in:
@@ -61,9 +61,16 @@ func (uc *AuthenticationUsecase) recordLogin(ctx context.Context, attempt *Login
|
||||
}
|
||||
|
||||
func (uc *AuthenticationUsecase) Login(ctx context.Context, attempt *LoginAttempt) (*AuthenticationResult, error) {
|
||||
config, _ := uc.security.Current(ctx)
|
||||
config, err := uc.security.Current(ctx)
|
||||
if err != nil || config == nil {
|
||||
return nil, fmt.Errorf("%w: security config unavailable", ErrLoginState)
|
||||
}
|
||||
if config != nil && config.LockEnable {
|
||||
if locked, _ := uc.security.LoginLocked(ctx, attempt.Username); locked {
|
||||
locked, lockErr := uc.security.LoginLocked(ctx, attempt.Username)
|
||||
if lockErr != nil {
|
||||
return nil, fmt.Errorf("%w: %v", ErrLoginState, lockErr)
|
||||
}
|
||||
if locked {
|
||||
uc.recordLogin(ctx, attempt, false, "账号已锁定", 0)
|
||||
return nil, &AccountLockedError{Minutes: config.LockDuration}
|
||||
}
|
||||
@@ -75,7 +82,10 @@ func (uc *AuthenticationUsecase) Login(ctx context.Context, attempt *LoginAttemp
|
||||
if config.CaptchaTimeout > 0 {
|
||||
ipTTL = time.Duration(config.CaptchaTimeout) * time.Second
|
||||
}
|
||||
failures, _ := uc.security.EnsureLoginIPCounter(ctx, attempt.IP, ipTTL)
|
||||
failures, counterErr := uc.security.EnsureLoginIPCounter(ctx, attempt.IP, ipTTL)
|
||||
if counterErr != nil {
|
||||
return nil, fmt.Errorf("%w: %v", ErrLoginState, counterErr)
|
||||
}
|
||||
requireCaptcha = config.CaptchaOpen == 0 || failures > config.CaptchaOpen
|
||||
}
|
||||
if requireCaptcha && !uc.security.VerifyCaptcha(ctx, attempt.CaptchaID, attempt.Captcha, true) {
|
||||
@@ -89,9 +99,14 @@ func (uc *AuthenticationUsecase) Login(ctx context.Context, attempt *LoginAttemp
|
||||
_, _ = uc.security.IncrementLoginIP(ctx, attempt.IP, ipTTL)
|
||||
if config != nil && config.LockEnable {
|
||||
lockTTL := time.Duration(config.LockDuration) * time.Minute
|
||||
failures, _ := uc.security.IncrementLoginFailure(ctx, attempt.Username, lockTTL)
|
||||
failures, stateErr := uc.security.IncrementLoginFailure(ctx, attempt.Username, lockTTL)
|
||||
if stateErr != nil {
|
||||
return nil, fmt.Errorf("%w: %v", ErrLoginState, stateErr)
|
||||
}
|
||||
if int(failures) >= config.LockThreshold {
|
||||
_ = uc.security.LockLogin(ctx, attempt.Username, lockTTL)
|
||||
if stateErr = uc.security.LockLogin(ctx, attempt.Username, lockTTL); stateErr != nil {
|
||||
return nil, fmt.Errorf("%w: %v", ErrLoginState, stateErr)
|
||||
}
|
||||
}
|
||||
}
|
||||
uc.recordLogin(ctx, attempt, false, "用户名不存在或者密码错误", 0)
|
||||
@@ -130,6 +145,52 @@ func (uc *AuthenticationUsecase) Login(ctx context.Context, attempt *LoginAttemp
|
||||
return &AuthenticationResult{User: user, Token: issued.Value, ExpiresAt: issued.ExpiresAt, NeedChangePassword: user.MustChangePassword}, nil
|
||||
}
|
||||
|
||||
func userHasAuthority(user *User, authorityID uint) bool {
|
||||
if user == nil || authorityID == 0 {
|
||||
return false
|
||||
}
|
||||
if user.AuthorityID == authorityID {
|
||||
return true
|
||||
}
|
||||
for _, authority := range user.Authorities {
|
||||
if authority.AuthorityID == authorityID {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (uc *AuthenticationUsecase) currentTokenUser(ctx context.Context, claims *AuthClaims) (*User, error) {
|
||||
if claims == nil || claims.UUID == "" || claims.IssuedAt.IsZero() {
|
||||
return nil, ErrTokenDisabled
|
||||
}
|
||||
user, err := uc.users.UserByUUID(ctx, claims.UUID)
|
||||
if err != nil || user == nil || user.ID != claims.ID || user.Username != claims.Username {
|
||||
return nil, ErrTokenDisabled
|
||||
}
|
||||
if user.Enable != 1 {
|
||||
return nil, &UserDisabledError{UserID: user.ID}
|
||||
}
|
||||
if !userHasAuthority(user, claims.AuthorityID) {
|
||||
return nil, ErrTokenDisabled
|
||||
}
|
||||
passwordVersion := int64(0)
|
||||
if user.PasswordUpdatedAt != nil {
|
||||
passwordVersion = user.PasswordUpdatedAt.UnixNano()
|
||||
}
|
||||
if passwordVersion != claims.PasswordVersion {
|
||||
return nil, ErrTokenDisabled
|
||||
}
|
||||
config, err := uc.security.Current(ctx)
|
||||
if err != nil || config == nil {
|
||||
return nil, ErrTokenDisabled
|
||||
}
|
||||
if config.PwdExpireEnable && config.PwdExpireDays > 0 && user.PasswordUpdatedAt != nil && time.Now().After(user.PasswordUpdatedAt.AddDate(0, 0, config.PwdExpireDays)) {
|
||||
user.MustChangePassword = true
|
||||
}
|
||||
return user, nil
|
||||
}
|
||||
|
||||
// SwitchAuthority changes the current user's active role and re-signs the
|
||||
// current claims. Role switching keeps the original JWT expiry; it
|
||||
// does not treat a role switch as a fresh login or rotate the multipoint
|
||||
@@ -166,12 +227,17 @@ func (uc *AuthenticationUsecase) AuthenticateToken(ctx context.Context, token st
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
user, err := uc.currentTokenUser(ctx, claims)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
claims.NickName = user.NickName
|
||||
claims.MustChangePwd = user.MustChangePassword
|
||||
result := &TokenAuthentication{Claims: claims}
|
||||
if claims.BufferTime <= 0 || time.Until(claims.ExpiresAt) >= claims.BufferTime {
|
||||
return result, nil
|
||||
}
|
||||
user := &User{ID: claims.ID, UUID: claims.UUID, Username: claims.Username, NickName: claims.NickName, AuthorityID: claims.AuthorityID, MustChangePassword: claims.MustChangePwd}
|
||||
issued, err := uc.issuer.IssueToken(user, claims.AuthorityID, claims.MustChangePwd, 0)
|
||||
issued, err := uc.issuer.IssueToken(user, claims.AuthorityID, user.MustChangePassword, 0)
|
||||
if err != nil {
|
||||
return result, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user