Shiro+Redis实现登录次数冻结的示例

论坛 期权论坛 脚本     
niminba   2021-5-23 02:54   1241   0

概述

假设我们需要有这样一个场景:如果用户连续输错5次密码,那可能说明有人在搞事情,所以需要暂时冻结该账户的登录功能

关于Shiro整合JWT,可以看这里:Springboot实现Shiro+JWT认证

假设我们的项目中用到了shiro,因为Shiro是建立在完善的接口驱动设计和面向对象原则之上的,支持各种自定义行为,所以我们可以结合Shiro框架的认证模块和redis来实现这个功能。

思路

我们大体的思路如下:

image-20200225151338221

  • 用户登录
  • Shiro去Redis检查账户的登录错误次数是否超过规定范围(超过了就是所谓的冻结)
  • Shiro进行密码比对
  • 如果登录失败,则去Redis里记录:登录错误次数+1
  • 如果密码正确,则登录成功,删除Redis里的登录错误记录

前期准备

除了需要用到Shiro以外,我们也需要用到Redis,这里需要先配置好RedisTemplate,(由于这个不是重点,我就把代码和配置方法贴在文章的最后了),另外,在Controller层,登录接口的异常处理除了之前的登录错误,还需要新增一个账户冻结类的异常,代码如下:

 @PostMapping(value = "/login")
 public AccountVO login(String userName, String password){
  
  //尝试登录
  Subject subject = SecurityUtils.getSubject();
  try {
   //通过shiro提供的安全接口来进行认证
   subject.login(new UsernamePasswordToken(userName, password));
  } catch (ExcessiveAttemptsException e1) {
   //新增一个账户锁定类错误
   throw new AccountLockedException();
  } catch (Exception e) {
   //其他的错误判定
   throw new LoginFailed();
  }
  //聚合登录信息
  AccountVO account = accountService.getAccountByUserName(userName);
  //返回正确登录的结果
  return account;
 }

自定义Shiro认证管理器

HashedCredentialsMatcher

当你在上面的Controller层调用subject.login方法后,会进入到自定义的Realm里去,然后慢慢进入到Shiro当前的Security Manager里定义的HashedCredentialsMatcher认证管理器的doCredentialsMatch方法,进行密码匹配,原版代码如下:

 /**
  * This implementation first hashes the {@code token}'s credentials, potentially using a
  * {@code salt} if the {@code info} argument is a
  * {@link org.apache.shiro.authc.SaltedAuthenticationInfo SaltedAuthenticationInfo}. It then compares the hash
  * against the {@code AuthenticationInfo}'s
  * {@link #getCredentials(org.apache.shiro.authc.AuthenticationInfo) already-hashed credentials}. This method
  * returns {@code true} if those two values are {@link #equals(Object, Object) equal}, {@code false} otherwise.
  *
  * @param token the {@code AuthenticationToken} submitted during the authentication attempt.
  * @param info the {@code AuthenticationInfo} stored in the system matching the token principal
  * @return {@code true} if the provided token credentials hash match to the stored account credentials hash,
  *   {@code false} otherwise
  * @since 1.1
  */
 @Override
 public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
  Object tokenHashedCredentials = hashProvidedCredentials(token, info);
  Object accountCredentials = getCredentials(info);
  return equals(tokenHashedCredentials, accountCredentials);
 }

可以发现,原版的逻辑很简单,就做了两件事,获取密码,比对密码。

由于我们需要联动Redis,在每次登录前都做一次冻结检查,每次遇到登录失败之后还需要实现对redis的写操作,所以现在需要重写一个认证管理器去配置到Security Manager里。

CustomMatcher

我们自定义一个CustomMatcher,这个类继承了HashedCredentialsMatcher,唯独重写了doCredentialsMatch方法,在这里面加入了我们自己的逻辑,代码如下:

import com.imlehr.internship.redis.RedisStringService;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.ExcessiveAttemptsException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.springframework.beans.factory.annotation.Autowired;

/**
 * @author Lehr
 * @create: 2020-02-25
 */
public class CustomMatcher extends HashedCredentialsMatcher {

 //这个是redis里的key的统一前缀
 private static final String PREFIX = "USER_LOGIN_FAIL:";

 @Autowired
 RedisStringService redisUtils;

 @Override
 public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {

  //检查本账号是否被冻结

  //先获取用户的登录名字 
  UsernamePasswordToken myToken = (UsernamePasswordToken) token;

  String userName = myToken.getUsername();

  //初始化错详7FVB7G&VF5FVFR&VF5FVFSРТXiXZW&VF>{ZW&^ZkKi{n{N&WТ&fPТ&WGW&ТV&2&WBf7G&W7G&fRТ&W7VfSТG'ТfTFF&VF5FVFR4fRТF6WBWfRТ&W7VG'VSТ6F6W6WFТRD76vRТ&WGW&W7VТТXiXZW&VF>{Z&^ZkKi{n{N&WТ&fPТ&W&PТ&WGW&ТV&2&WBf7G&W7G&fRW&RТ&W7VfSТG'ТfTFF&VF5FVFR4fRТF6WBWfRТ&VF5FVFR&RWW&RFV4Т&W7VG'VSТ6F6W6WFТRD76vRТ&WGW&W7VТРТ&VF>{ZТ&WТ&WGW&ТV&27BvWBf7G&WТ7B&W7VТG'ТfTFF&VF5FVFR4fRТ&W7VFvWBWТ6F6W6WFТRD76vRТ&WGW&W7VТТXNijVF>{ZKZyFWТ&WТ&WGW&ТV&2&7G2f7G&WТ&W7VfSТG'Т&W7V&VF5FVFR4WWТ6F6W6WFТRD76vRТ&WGW&W7VТТ&VF>jhWXNZyGfPТ&WТ&WGW&ТV&2&VfRf7G&WТ&W7VfSТG'ТbW7G2WТ&VF5FVFRFRWТ&W7VG'VSТ6F6W6WFТRD76vRТ&WGW&W7VТТ&VF>jhW>hxNZyGfPТ&W0Т&WGW&ТV&2f&VfRf7G&W2ТfG&WW2Т&VfRWТУFcУjX[>K&F>Z[ji[Xk{>yNih~z[K{NZIyX[56&F>y[Xk{>Xh^Z{J.zKK^XNih~zhn{~{XyNyX[>ih~z[ZJ~Z^YZIiJ
分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:1060120
帖子:212021
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP