SpringBoot Shiro实现登录失败次数限制(三)

1. 修改CustomRealm.java中 doGetAuthenticationInfo 方法

之前我们会在里面进行密码的判断,现在我们把密码判断交给密码比较器

  /**
     * 登录查询
     *
     * @param authenticationToken
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException{

        //获取用户名
        UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) authenticationToken;
        String userName = usernamePasswordToken.getUsername();

        SysUser sysUser = new SysUser();
        sysUser.setSysUserName(userName);
        List<SysUser> sysUserList = sysUserService.queryAll(sysUser);

        if(sysUserList.size() <= 0){
            throw new UnknownAccountException("用户名不存在!");
        }
        
        int userId = sysUserList.get(0).getSysUserId();
        String userStatus = sysUserList.get(0).getSysUserStatus();
        String userPassword = sysUserList.get(0).getSysUserPassword();

        if("1".equals(userStatus)){
            throw new LockedAccountException("用户已锁定,请联系管理员!");
        }

        SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(userId,userPassword,getName());
        return simpleAuthenticationInfo;
    }

2. 新增RetryLimitHashedCredentialsMatcher.java

package com.pk.ass.config;

import com.pk.ass.entity.SysUser;
import com.pk.ass.service.SysUserService;
import org.apache.shiro.authc.*;
import org.apache.shiro.authc.credential.SimpleCredentialsMatcher;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

public class RetryLimitHashedCredentialsMatcher extends SimpleCredentialsMatcher {
    private static final Logger logger = LoggerFactory.getLogger(RetryLimitHashedCredentialsMatcher.class);
    private final int ALLCOUNT = 5;

    @Autowired
    private SysUserService sysUserService;
    private Cache<String, AtomicInteger> passwordRetryCache;

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

    @Override
    public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
        String userName = token.getPrincipal().toString();

        SysUser sysUser = new SysUser();
        sysUser.setSysUserName(userName);
        List<SysUser> sysUserList = sysUserService.queryAll(sysUser);
        sysUser = sysUserList.get(0);

        String userId = sysUser.getSysUserId().toString();

        //获取缓存中的登录次数
        AtomicInteger retryCount = passwordRetryCache.get(userId);

        // 用户没有登录过,将用户添加到缓存中
        if (retryCount == null) {
            retryCount = new AtomicInteger(0);
            passwordRetryCache.put(userId, retryCount);
        }

        // 登录次数大于5,锁定账号
        if (retryCount.incrementAndGet() > ALLCOUNT) {

            sysUser.setSysUserStatus("1");
            sysUserService.update(sysUser);
            logger.info("用户:{},锁定!", sysUser.getSysUserName());

            //抛出用户锁定异常
            throw new LockedAccountException("登录次数大于5次,已锁定账户,请联系管理员!");

        }

        // 判断用户账号和密码是否正确
        boolean matches = super.doCredentialsMatch(token, info);
        if (matches) {
            passwordRetryCache.remove(userId);
        } else {

            int surplusCount = ALLCOUNT - passwordRetryCache.get(userId).intValue();

            //抛出密码错误异常
             throw new IncorrectCredentialsException("密码错误,总登录次数"+ALLCOUNT+"次,剩余次数: " + surplusCount);

        }

        return matches;
    }
}


3. 在ShiroConfig.java增加

   /**
     * 配置密码比较器
     * @return
     */
    @Bean("credentialsMatcher")
    public RetryLimitHashedCredentialsMatcher retryLimitHashedCredentialsMatcher(){
        RetryLimitHashedCredentialsMatcher retryLimitHashedCredentialsMatcher = new RetryLimitHashedCredentialsMatcher(ehCacheManager());

        //如果密码加密,可以打开下面配置
        //加密算法的名称
        //retryLimitHashedCredentialsMatcher.setHashAlgorithmName("MD5");
        //配置加密的次数
        //retryLimitHashedCredentialsMatcher.setHashIterations(1024);
        //是否存储为16进制
        //retryLimitHashedCredentialsMatcher.setStoredCredentialsHexEncoded(true);

        return retryLimitHashedCredentialsMatcher;
    }

4. 在ShiroConfig.java的myShiroRealm方法中增加

//配置自定义密码比较器
customRealm.setCredentialsMatcher(retryLimitHashedCredentialsMatcher());

    //将自己的验证方式加入容器
    @Bean
    public CustomRealm myShiroRealm() {
        CustomRealm customRealm = new CustomRealm();
        customRealm.setCachingEnabled(true);

        //启用身份验证缓存,即缓存AuthenticationInfo信息,默认false
        customRealm.setAuthenticationCachingEnabled(true);

        //缓存AuthenticationInfo信息的缓存名称 在ehcache-shiro.xml中有对应缓存的配置
        customRealm.setAuthenticationCacheName("authenticationCache");

        //启用授权缓存,即缓存AuthorizationInfo信息,默认false
        customRealm.setAuthorizationCachingEnabled(true);

        //缓存AuthorizationInfo信息的缓存名称  在ehcache-shiro.xml中有对应缓存的配置
        customRealm.setAuthorizationCacheName("authorizationCache");

        //配置自定义密码比较器
        customRealm.setCredentialsMatcher(retryLimitHashedCredentialsMatcher());

        return customRealm;
    }

5. 在ehcache-shiro.xml中新增缓存区域

    <!-- 登录失败次数缓存
     注意 timeToLiveSeconds 设置为300秒 也就是5分钟
    -->
    <cache name="passwordRetryCache"
           maxEntriesLocalHeap="2000"
           eternal="false"
           timeToIdleSeconds="0"
           timeToLiveSeconds="300"
           overflowToDisk="false"
           statistics="true">
    </cache>

6. 用户登录方法

    @RequestMapping("login")
    @ResponseBody
    public Object login(String userName,String password,boolean rememberMe){
        ResultEntity resultEntity = new ResultEntity();
        Subject subject = SecurityUtils.getSubject();
        password = password + userName;
        String md5Str = DigestUtils.md5DigestAsHex(DigestUtils.md5DigestAsHex(password.getBytes()).getBytes());
        UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(userName,md5Str,rememberMe);
        try{
            subject.login(usernamePasswordToken);
        }catch (Exception e){
            resultEntity.setResult(false);
            resultEntity.setMessage(e.getMessage());
            return resultEntity;
        }

        resultEntity.setResult(true);
        resultEntity.setMessage("登录成功");
        return resultEntity;
    }

7. 验证页面

在这里插入图片描述
参考文章

王赛超 - springboot整合shiro-登录失败次数限制(八)
饭一碗 - 原子操作类AtomicInteger详解

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值