forked from ruoyi/RuoYi-Vue
		
	支持配置密码最大错误次数/锁定时间
This commit is contained in:
		| @@ -0,0 +1,28 @@ | ||||
| package com.ruoyi.framework.security.context; | ||||
|  | ||||
| import org.springframework.security.core.Authentication; | ||||
|  | ||||
| /** | ||||
|  * 身份验证信息 | ||||
|  *  | ||||
|  * @author ruoyi | ||||
|  */ | ||||
| public class AuthenticationContextHolder | ||||
| { | ||||
|     private static final ThreadLocal<Authentication> contextHolder = new ThreadLocal<>(); | ||||
|  | ||||
|     public static Authentication getContext() | ||||
|     { | ||||
|         return contextHolder.get(); | ||||
|     } | ||||
|  | ||||
|     public static void setContext(Authentication context) | ||||
|     { | ||||
|         contextHolder.set(context); | ||||
|     } | ||||
|  | ||||
|     public static void clearContext() | ||||
|     { | ||||
|         contextHolder.remove(); | ||||
|     } | ||||
| } | ||||
| @@ -23,6 +23,7 @@ import com.ruoyi.common.utils.StringUtils; | ||||
| import com.ruoyi.common.utils.ip.IpUtils; | ||||
| import com.ruoyi.framework.manager.AsyncManager; | ||||
| import com.ruoyi.framework.manager.factory.AsyncFactory; | ||||
| import com.ruoyi.framework.security.context.AuthenticationContextHolder; | ||||
| import com.ruoyi.system.service.ISysConfigService; | ||||
| import com.ruoyi.system.service.ISysUserService; | ||||
|  | ||||
| @@ -70,9 +71,10 @@ public class SysLoginService | ||||
|         Authentication authentication = null; | ||||
|         try | ||||
|         { | ||||
|             UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, password); | ||||
|             AuthenticationContextHolder.setContext(authenticationToken); | ||||
|             // 该方法会去调用UserDetailsServiceImpl.loadUserByUsername | ||||
|             authentication = authenticationManager | ||||
|                     .authenticate(new UsernamePasswordAuthenticationToken(username, password)); | ||||
|             authentication = authenticationManager.authenticate(authenticationToken); | ||||
|         } | ||||
|         catch (Exception e) | ||||
|         { | ||||
|   | ||||
| @@ -0,0 +1,94 @@ | ||||
| package com.ruoyi.framework.web.service; | ||||
|  | ||||
| import java.util.concurrent.TimeUnit; | ||||
| import org.springframework.beans.factory.annotation.Autowired; | ||||
| import org.springframework.beans.factory.annotation.Value; | ||||
| import org.springframework.security.core.Authentication; | ||||
| import org.springframework.stereotype.Component; | ||||
| import com.ruoyi.common.constant.CacheConstants; | ||||
| import com.ruoyi.common.constant.Constants; | ||||
| import com.ruoyi.common.core.domain.entity.SysUser; | ||||
| import com.ruoyi.common.core.redis.RedisCache; | ||||
| import com.ruoyi.common.exception.user.UserPasswordNotMatchException; | ||||
| import com.ruoyi.common.exception.user.UserPasswordRetryLimitExceedException; | ||||
| import com.ruoyi.common.utils.MessageUtils; | ||||
| import com.ruoyi.common.utils.SecurityUtils; | ||||
| import com.ruoyi.framework.manager.AsyncManager; | ||||
| import com.ruoyi.framework.manager.factory.AsyncFactory; | ||||
| import com.ruoyi.framework.security.context.AuthenticationContextHolder; | ||||
|  | ||||
| /** | ||||
|  * 登录密码方法 | ||||
|  *  | ||||
|  * @author ruoyi | ||||
|  */ | ||||
| @Component | ||||
| public class SysPasswordService | ||||
| { | ||||
|     @Autowired | ||||
|     private RedisCache redisCache; | ||||
|  | ||||
|     @Value(value = "${user.password.maxRetryCount}") | ||||
|     private int maxRetryCount; | ||||
|  | ||||
|     @Value(value = "${user.password.lockTime}") | ||||
|     private int lockTime; | ||||
|  | ||||
|     /** | ||||
|      * 登录账户密码错误次数缓存键名 | ||||
|      *  | ||||
|      * @param username 用户名 | ||||
|      * @return 缓存键key | ||||
|      */ | ||||
|     private String getCacheKey(String username) | ||||
|     { | ||||
|         return CacheConstants.PWD_ERR_CNT_KEY + username; | ||||
|     } | ||||
|  | ||||
|     public void validate(SysUser user) | ||||
|     { | ||||
|         Authentication usernamePasswordAuthenticationToken = AuthenticationContextHolder.getContext(); | ||||
|         String username = usernamePasswordAuthenticationToken.getName(); | ||||
|         String password = usernamePasswordAuthenticationToken.getCredentials().toString(); | ||||
|  | ||||
|         Integer retryCount = redisCache.getCacheObject(getCacheKey(username)); | ||||
|  | ||||
|         if (retryCount == null) | ||||
|         { | ||||
|             retryCount = 0; | ||||
|         } | ||||
|  | ||||
|         if (retryCount >= Integer.valueOf(maxRetryCount).intValue()) | ||||
|         { | ||||
|             AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, | ||||
|                     MessageUtils.message("user.password.retry.limit.exceed", maxRetryCount, lockTime))); | ||||
|             throw new UserPasswordRetryLimitExceedException(maxRetryCount, lockTime); | ||||
|         } | ||||
|  | ||||
|         if (!matches(user, password)) | ||||
|         { | ||||
|             retryCount = retryCount + 1; | ||||
|             AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, | ||||
|                     MessageUtils.message("user.password.retry.limit.count", retryCount))); | ||||
|             redisCache.setCacheObject(getCacheKey(username), retryCount, lockTime, TimeUnit.MINUTES); | ||||
|             throw new UserPasswordNotMatchException(); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             clearLoginRecordCache(username); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public boolean matches(SysUser user, String rawPassword) | ||||
|     { | ||||
|         return SecurityUtils.matchesPassword(rawPassword, user.getPassword()); | ||||
|     } | ||||
|  | ||||
|     public void clearLoginRecordCache(String loginName) | ||||
|     { | ||||
|         if (redisCache.hasKey(getCacheKey(loginName))) | ||||
|         { | ||||
|             redisCache.deleteObject(getCacheKey(loginName)); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -26,6 +26,9 @@ public class UserDetailsServiceImpl implements UserDetailsService | ||||
|  | ||||
|     @Autowired | ||||
|     private ISysUserService userService; | ||||
|      | ||||
|     @Autowired | ||||
|     private SysPasswordService passwordService; | ||||
|  | ||||
|     @Autowired | ||||
|     private SysPermissionService permissionService; | ||||
| @@ -50,6 +53,8 @@ public class UserDetailsServiceImpl implements UserDetailsService | ||||
|             throw new ServiceException("对不起,您的账号:" + username + " 已停用"); | ||||
|         } | ||||
|  | ||||
|         passwordService.validate(user); | ||||
|  | ||||
|         return createLoginUser(user); | ||||
|     } | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 RuoYi
					RuoYi