@Autowired
private ShiroSessionManager sessionManager;
@GetMapping("/login")
public KeyValue login(@Validated User user, Boolean rememberMe, BindingResult result, HttpServletRequest request){
String mySessionId = request.getSession().getId();
UsernamePasswordToken token = new UsernamePasswordToken(user.getUsername(),user.getPassword());
token.setRememberMe(rememberMe);
try {
SecurityUtils.getSubject().login(token);
//处理session
//获取当前已登录的用户session列表(除自己外)
Collection<Session> sessions = sessionManager.getSessionDAO().getActiveSessions();
User temp;
for(Session session : sessions) {
//清除该用户以前登录时保存的session,强制退出
//用户信息存储的key
Object attribute = session.getAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY);
if (attribute == null) {
continue;
}
//获取对象信息
temp = (User) ((SimplePrincipalCollection) attribute).getPrimaryPrincipal();
if (user.getUsername().equals(temp.getUsername()) && !session.getId().equals(mySessionId)) {
sessionManager.getSessionDAO().delete(session);
}
}
return KeyValue.ok("登录成功");
} catch (IncorrectCredentialsException e) {
return KeyValue.err("密码错误");
} catch (LockedAccountException e) {
return KeyValue.err("登录失败,该用户已被冻结");
} catch (AuthenticationException e) {
return KeyValue.err("登录失败,该用户不存在");
} catch (Exception e) {
e.printStackTrace();
return KeyValue.err("操作异常");
}
}
登录时根据redis中的session控制登录人数
/**
* 自定义配置密码比较器
*
*/
@Bean("credentialsMatcher")
public RetryLimitHashedCredentialsMatcher retryLimitHashedCredentialsMatcher(){
RetryLimitHashedCredentialsMatcher retryLimitHashedCredentialsMatcher = new RetryLimitHashedCredentialsMatcher();
//如果密码加密,可以打开下面配置
//加密算法的名称
retryLimitHashedCredentialsMatcher.setHashAlgorithmName("md5");
//配置加密的次数
retryLimitHashedCredentialsMatcher.setHashIterations(2);
//是否存储为16进制
retryLimitHashedCredentialsMatcher.setStoredCredentialsHexEncoded(true);
return retryLimitHashedCredentialsMatcher;
}
来控制登录次数
/**
* 登录次数限制
*/
@Slf4j
public class RetryLimitHashedCredentialsMatcher extends HashedCredentialsMatcher {
public static final String DEFAULT_RETRYLIMIT_CACHE_KEY_PREFIX = "shiro:cache:retrylimit:";
private String keyPrefix = DEFAULT_RETRYLIMIT_CACHE_KEY_PREFIX;
private static final Integer LOCK = 1;
private static final Integer NORMAL = 0;
private static final Integer LOCK_NUM = 5;
@Autowired
private UserMapper userMapper;
@Autowired
@Qualifier("redisTemplate")
private RedisTemplate redisManager;
public RetryLimitHashedCredentialsMatcher() {
}
private String getRedisKickoutKey(String username) {
return this.keyPrefix + username;
}
@Override
public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
//获取用户名
String username = (String)token.getPrincipal();
//获取用户登录次数
Integer retryCount = NORMAL;
if (redisManager.hasKey(getRedisKickoutKey(username))){
retryCount = Integer.valueOf((String) redisManager.opsForValue().get(getRedisKickoutKey(username)));
}
if (retryCount > LOCK_NUM) {
//如果用户登陆失败次数大于5次 抛出锁定用户异常 并修改数据库字段
User user = userMapper.findUserByUserName(username);
if (user != null && user.getState().equals(NORMAL)) {
//数据库字段 默认为 0 就是正常状态 所以 要改为1
//修改数据库的状态字段为锁定
user.setState(LOCK);
userMapper.updateByPrimaryKey(user);
}
log.info("---------------------锁定用户" + user.getUsername()+"--------------------------");
//抛出用户锁定异常
throw new LockedAccountException();
}
//判断用户账号和密码是否正确
boolean matches = super.doCredentialsMatch(token, info);
if (matches) {
//如果正确,从缓存中将用户登录计数 清除
redisManager.delete(getRedisKickoutKey(username));
}else{
redisManager.opsForValue().increment(getRedisKickoutKey(username),1);
}
return matches;
}
/**
* 根据用户名 解锁用户
* @param username
* @return
*/
public void unlockAccount(String username){
User user = userMapper.findUserByUserName(username);
if (user != null){
//修改数据库的状态字段为锁定
user.setState(NORMAL);
userMapper.updateByPrimaryKey(user);
redisManager.delete(getRedisKickoutKey(username));
}
}
}