Shiro功能应用(六)--登陆失败重试次数控制

     主要就是用来限制用户登录尝试次数的,登陆失不失败,与密码认证有关,所以要自定义一个密码匹配器,继承原来的HashedCredentialsMatcher密码匹配器,重写验证方法doCredentialsMatch。本文在上一篇文章Shiro功能应用(五)–Session管理的登陆人数控制代码基础进行添加登陆次数限制。

代码实现:

      代码地址:
          https://github.com/OooooOz/SpringBoot-Shiro

     ShiroConfig的自定义Realm注入自定义密码比较器:

 @Bean(name="shiroRealm")
    public MyShiroRealm getMyShiroRealm(@Qualifier("credentialsMatcherLimit")HashedCredentialsMatcher credentialsMatcher){
        MyShiroRealm shiroRealm = new MyShiroRealm();
        //shiroRealm.setCredentialsMatcher(credentialsMatcher);//设置密码比较器,beanId=credentialsMatcher
        shiroRealm.setCredentialsMatcher(credentialsMatcher);//设置自定义密码比较器,beanId=credentialsMatcherLimit
       ... ... ... ... 
        return shiroRealm;
    }

     ShiroConfig的自定义密码比较器:

    @Bean("credentialsMatcherLimit")
    public RetryLimitHashedCredentialsMatcher retryLimitHashedCredentialsMatcher(){
        RetryLimitHashedCredentialsMatcher retryLimitHashedCredentialsMatcher = new RetryLimitHashedCredentialsMatcher(getEhCacheManager());
        retryLimitHashedCredentialsMatcher.setHashAlgorithmName("MD5");	//加密算法的名称
        retryLimitHashedCredentialsMatcher.setHashIterations(1);		//配置加密的次数
        retryLimitHashedCredentialsMatcher.setRetryLimitNum(3);		//配置加密的次数
        //retryLimitHashedCredentialsMatcher.setStoredCredentialsHexEncoded(true);//是否存储为16进制
        return retryLimitHashedCredentialsMatcher;
    }

     自定义密码比较器类:

package com.demo.config;

import java.util.concurrent.atomic.AtomicInteger;

import com.demo.dao.IUserMapper;
import com.demo.entity.User;

import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.authc.credential.SimpleCredentialsMatcher;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheManager;
import org.springframework.beans.factory.annotation.Autowired;


/**
 * @description: 登陆次数限制
 */
public class RetryLimitHashedCredentialsMatcher extends HashedCredentialsMatcher {

    @Autowired
    private IUserMapper userMapper;
    private Cache<String, AtomicInteger> passwordRetryCache;
    private int retryLimitNum;

    public RetryLimitHashedCredentialsMatcher(CacheManager cacheManager) {
        passwordRetryCache = cacheManager.getCache("passwordRetryCache");
    }

    public void setRetryLimitNum(int retryLimitNum) {
        this.retryLimitNum = retryLimitNum;
    }

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

        //获取用户名
        String username = (String)token.getPrincipal();
        //获取用户登录次数
        AtomicInteger retryCount = passwordRetryCache.get(username);
        if (retryCount == null) {
            //如果用户没有登陆过,登陆次数加1 并放入缓存
            retryCount = new AtomicInteger(0);
            passwordRetryCache.put(username, retryCount);
        }
        if (retryCount.incrementAndGet() >retryLimitNum) {
            //如果用户登陆失败次数大于retryLimitNum, 抛出锁定用户异常  并修改数据库字段
            User user = userMapper.findByUserName(username);
            if (user != null && 0==user.getStatus()){
                //数据库字段 默认为 0  就是正常状态 所以 要改为1
                //修改数据库的状态字段为锁定
                user.setStatus(1);
                userMapper.update(user);
            }
            System.out.println("锁定用户" + user.getUserName());
            //抛出用户锁定异常
            throw new LockedAccountException();
        }
        //判断用户账号和密码是否正确
        boolean matches = super.doCredentialsMatch(token, info);
        if (matches) {
            //如果正确,从缓存中将用户登录计数 清除
            passwordRetryCache.remove(username);
        }
        return matches;
    }

    /**
     * 根据用户名 解锁用户
     */
    public void unlockAccount(String username){
        User user = userMapper.findByUserName(username);
        if (user != null){
            //修改数据库的状态字段为锁定
            user.setStatus(0);
            userMapper.update(user);
            passwordRetryCache.remove(username);
        }
    }

}

     EHCache缓存配置文件:

	<!-- 登录失败次数缓存
	     注意 timeToLiveSeconds 设置为300秒 也就是5分钟
	     可以根据自己的需求更改
	 -->
	<cache name="passwordRetryCache"
	       maxEntriesLocalHeap="2000"
	       eternal="false"
	       timeToIdleSeconds="0"
	       timeToLiveSeconds="300"
	       overflowToDisk="false"
	       statistics="true">
	</cache>

执行过程:

     在Shiro内部认证方法中,执行doCredentialsMatch会跳转到自定义密码匹配器的方法。使用了EHCache(构造方法注入的new RetryLimitHashedCredentialsMatcher(getEhCacheManager());)
     第一次登陆,缓存无数据,retryCount=null,初始化次数=0,然后将次数put进缓存,在调用父类的super.doCredentialsMatch(token, info); 时,如果通过认证,则将之前放入的缓存移除调,如果认证失败,则retryCount会自动加1,(retryCount为AtomicInteger类型),此时为第一次登陆失败。
     如果登陆次数大于指定次数,则将用户状态更新,然后抛出用户锁定异常。缓存设置类300s失效,所以300s内是无法登陆的,300s后才能登陆。
     在控制器中捕获异常,输出异常信息到前台
在这里插入图片描述
在这里插入图片描述
     参考文章
     springboot整合shiro-登录失败次数限制(八)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章