HashedCredentialsMatcher实现密码验证服务
Shiro提供了CredentialsMatcher的散列实现HashedCredentialsMatcher,和之前的PasswordMatcher不同的是,它只用于密码验证,且可以提供自己的盐,而不是随机生成盐,且生成密码散列值的算法需要自己写,因为能提供自己的盐。
1、生成密码散列值
此处我们使用MD5算法,“密码+盐(用户名+随机数)”的方式生成散列值:
- String algorithmName = "md5";
- String username = "liu";
- String password = "123";
- String salt1 = username;
- String salt2 = new SecureRandomNumberGenerator().nextBytes().toHex();
- int hashIterations = 2;
- SimpleHash hash = new SimpleHash(algorithmName, password, salt1 + salt2, hashIterations);
- String encodedPassword = hash.toHex();
如果要写用户模块,需要在新增用户/重置密码时使用如上算法保存密码,将生成的密码及salt2存入数据库(因为我们的散列算法是:md5(md5(密码+username+salt2)))
密码重试次数限制
如在1个小时内密码最多重试5次,如果尝试次数超过5次就锁定1小时,1小时后可再次重试,如果还是重试失败,可以锁定如1天,以此类推,防止密码被暴力破解。我们通过继承HashedCredentialsMatcher,且使用Ehcache记录重试次数和超时时间。
com.github.zhangkaitao.shiro.chapter5.hash.credentials.RetryLimitHashedCredentialsMatcher:
- 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;
- }
如上代码逻辑比较简单,即如果密码输入正确清除cache中的记录;否则cache中的重试次数+1,如果超出5次那么抛出异常表示超出重试次数了