Shiro安全框架学习04 - 登录失败次数限制
Posted 麦田
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Shiro安全框架学习04 - 登录失败次数限制相关的知识,希望对你有一定的参考价值。
为了防止被恶意暴力破解,我们都会进行登录失败超过一定次数进行锁定账号禁止登录。使用Ehcache提供缓存服务。
在前几篇代码的基础上添加ehcache依赖
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache-core</artifactId>
<version>2.6.6</version>
</dependency>
配置ehcache.xml
<?xml version="1.0" encoding="UTF-8"?>
<ehcache name="es">
<diskStore path="java.io.tmpdir"/>
<!-- 登录记录缓存 锁定10分钟 -->
<cache name="passwordRetryCache"
maxEntriesLocalHeap="2000"
eternal="false"
timeToIdleSeconds="3600"
timeToLiveSeconds="0"
overflowToDisk="false"
statistics="true">
</cache>
</ehcache>
编写自定义的CredentialsMatcher
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Ehcache;
import net.sf.ehcache.Element;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.ExcessiveAttemptsException;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.cache.Cache;
import java.util.concurrent.atomic.AtomicInteger;
public class RetryLimitHashedCredentialsMatcher extends
HashedCredentialsMatcher
private Ehcache passwordRetryCache;
public RetryLimitHashedCredentialsMatcher()
CacheManager cacheManager = CacheManager.newInstance(CacheManager.class.getClassLoader().getResource("ehcache.xml"));
passwordRetryCache = cacheManager.getCache("passwordRetryCache");
@Override
public boolean doCredentialsMatch(AuthenticationToken token,
AuthenticationInfo info)
String username = (String)token.getPrincipal();
//retry count + 1
Element element = passwordRetryCache.get(username);
if(element == null)
element = new Element(username , new AtomicInteger(0));
passwordRetryCache.put(element);
AtomicInteger retryCount = (AtomicInteger)element.getObjectValue();
if(retryCount.incrementAndGet() > 5)
//if retry count > 5 throw
throw new ExcessiveAttemptsException();
boolean matches = super.doCredentialsMatch(token, info);
if(matches)
//clear retry count
passwordRetryCache.remove(username);
return matches;
编写自定义的Realm
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authc.credential.PasswordService;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.crypto.SecureRandomNumberGenerator;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
public class UserRealm extends AuthorizingRealm
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection arg0)
// TODO Auto-generated method stub
return null;
@Override
protected AuthenticationInfo doGetAuthenticationInfo(
AuthenticationToken token) throws AuthenticationException
// TODO Auto-generated method stub
//获取账号密码
UsernamePasswordToken t = (UsernamePasswordToken) token;
String password = new String(t.getPassword());
String username = (String) token.getPrincipal();
//获取数据库种的密码、盐(此处写死,测试用)
User user = new User();
user.setUsername(username);
user.setPassword("b4b88b2ef28089f9c802df11bd216a33");
user.setSalt("713183f112f48824613f0f05d0d10060");
if (!"itmyhome".equals(username))
// 抛出 帐号找不到异常
throw new UnknownAccountException();
SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(
user.getUsername(), // 用户名
user.getPassword(), // 密码
ByteSource.Util.bytes(password + user.getSalt()),// salt=password+salt
getName() // realm name
);
return authenticationInfo;
测试类
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.ExcessiveAttemptsException;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.Factory;
import org.apache.shiro.mgt.SecurityManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ShiroTest
private static Logger logger = LoggerFactory.getLogger(ShiroTest.class);
public static void main(String[] args)
//获取SecurityManager工厂,此处使用Ini配置文件初始化SecurityManager
Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:auth.ini");
//得到SecurityManager实例 并绑定给SecurityUtils
SecurityManager securityManager = factory.getInstance();
SecurityUtils.setSecurityManager(securityManager);
//得到Subject及创建用户名/密码身份验证Token(即用户身份/凭证)
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken("itmyhome","1234561");
//模拟多次登录,密码错误超过5次即锁定账户
for (int i = 1; i <= 6; i++)
try
//登录,即身份验证
subject.login(token);
catch(ExcessiveAttemptsException e)
System.out.println("###### 账户已锁定 ########");
catch(IncorrectCredentialsException e)
System.out.println("###### 密码错误 ########");
catch(AuthenticationException e)
//身份验证失败
System.out.println("###### 账号错误 ########");
System.out.println("User is authenticated: " + subject.isAuthenticated());
示例源码地址:xxx
以上是关于Shiro安全框架学习04 - 登录失败次数限制的主要内容,如果未能解决你的问题,请参考以下文章