shiro 使用md5密码加密 锁定账户

此篇博客根据之前写的shiro快速配置延续的,建议不了解的可以先看看之前的博客。

springMVC中快速配置shiro

1.为了使用密码加密,我们新建一个对用户信息操作的工具类

package com.bf.planner.util;

import org.apache.shiro.crypto.SecureRandomNumberGenerator;
import org.apache.shiro.crypto.hash.SimpleHash;

import com.bf.planner.model.User;

public class EncryptUtil {

	private static String algorithmName = "MD5"; // 加密方式

	private static int hashIterations = 1024; // 加密次数

	public static User EncryptUser(User user) {

		// 随机盐对象
		SecureRandomNumberGenerator secureRandomNumberGenerator = new SecureRandomNumberGenerator();
		// 根据用户名以及随机生成的盐来拼接成密码盐
		String salt = user.getUser_name() + secureRandomNumberGenerator.nextBytes().toHex();

		// 获取加密后的密码
		String passWord = new SimpleHash(algorithmName, user.getPassword(), salt, hashIterations).toHex();

		user.setSalt(salt);	//设置密码盐

		user.setPassword(passWord);	//设置加密过后的密码

		return user;
	}

}
tips:加密方式可以修改,推荐使用MD5等散列式加密方式,因为此种加密方式不可逆;密码盐我是使用用户名拼接了生成的随机盐,此处可根据需求添加私盐,或定义固定盐,推荐使用私盐加随机盐;密码盐一定要存放起来,并且不要使用ByteSource.Util.bytes()方法进行转译,因为后面我们登录时shiro需要对其进行此方法的转译。


2.用户注册或修改信息时,调用上面的工具类进行盐的设置以及密码的加密:

EncryptUtil.EncryptUser(user);

tips:此处我们传递过去的其实是user对象的引用,所以不需要接收返回值。


3.密码加密已经完成,接下来我们需要对其解密,首先在springmvc配置中配置密码解析

<!-- 数据库保存的密码是使用MD5算法加密的,所以这里需要配置一个密码匹配对象 -->  
	<bean id="credentialsMatcher" class="com.bf.planner.realm.MyMatcher">
		<constructor-arg ref="cacheManager"/>	<!-- 缓存 -->
		<property name="hashAlgorithmName" value="MD5"></property> <!-- 加密算法的名称 -->
		<property name="hashIterations" value="1024"></property> <!-- 配置加密的次数 -->
		<property name="storedCredentialsHexEncoded" value="true"></property> <!-- 是否存储为16进制 -->
	</bean>

<!-- 自定义Realm -->
	<bean id="myRealm" class="com.bf.planner.realm.MyRealm">
		<property name="credentialsMatcher" ref="credentialsMatcher" />	<!-- 加密配置 -->
	</bean> 

tips:上面自定义了credentialsMatcher的实现类是为了做账户锁定,如果不需要的话,只要将class引向

org.apache.shiro.authc.credential.HashedCredentialsMatcher
类,去掉缓存的配置即可。这里的配置是为了在用户登录时对用户输入的密码的加密配置,因为上面我使用的是MD5加密,加密了1024次,所以此处配置这样。


4.配置基本完成,要想使用我们还需要在MyRealm中修改:

@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
		String user_name = (String) token.getPrincipal();// 获取登录者的用户名
		User user = loginService.getUser(user_name); // 根据用户名查出数据库中所对应的用户信息
		if (user == null) {
			throw new UnknownAccountException(); // 抛出账号不存在异常
		}
		
		AuthenticationInfo authcInfo = new SimpleAuthenticationInfo(user.getUser_name(), // 用户名
				user.getPassword(), // 密码
				ByteSource.Util.bytes(user.getSalt()), // 盐
				getName()); // realm name
		// 设置此用户的令牌,即设置正确的用户名与密码
		return authcInfo;
	}
tips:方法与原来的不同是在登录的验证处,我们多传递了一个盐,这是为了shiro对密码进行加密使用。


5.有的时候我们希望在用户多次输入错误密码时对其进行锁定操作,首先我们可以定义一个控制锁定密码时间的关于缓存的配置文件:

springmvc配置文件中引向我们的缓存配置文件:

<!-- 缓存管理 -->  
    <bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
    	<property name="cacheManagerConfigFile" value="classpath:ehcache.xml" />
    </bean> 

tips:此处指向了项目下的文件。

在ehcache.xml中我们配置如下:

<?xml version="1.0" encoding="UTF-8"?>
<ehcache name="shirocache" updateCheck="false">	<!-- updateCheck关闭网络获取缓存 -->

	<diskStore path="java.io.tmpdir" />  
  
    <!-- 登录记录缓存 锁定10分钟 -->  
    <cache name="passwordRetryCache" eternal="false"  
        timeToIdleSeconds="0" timeToLiveSeconds="600" overflowToDisk="false"  
        maxBytesLocalHeap="10M" statistics="true">  
    </cache>  
    <!-- timeToIdleSeconds 此属性设置后,限制时间以上次访问开始 
    	eg:设置时间为10分钟的话,我们在1分的时候账号被锁定,预定为10分时解锁.而我们在5分时再次输入密码(此时密码已被锁定,无论怎样都不会通过认证),那么解锁时间变为15分.
    	 timeToLiveSeconds 此属性设置后,限制时间以缓存创建开始
    	eg:设置时间为10分钟的话,我们在1分的时候账号被锁定,预定为10分时解锁.而我们在5分时再次输入密码(此时密码已被锁定,无论怎样都不会通过认证),那么解锁时间还是10分.
    	
    	maxBytesLocalHeap用来限制缓存所能使用的堆内存的最大字节数的,如果不设置则需设置另外一个属性,否则项目会编译出错,无法允许,此处不再详写。
    	 -->

</ehcache>
tips:updateCheck属性默认为true,有时会抛出异常Update check failed,建议设置为false。


6.配置完之后我们需要写我们自己的MyMatcher类:

package com.bf.planner.realm;

import java.util.concurrent.atomic.AtomicInteger;

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 org.apache.shiro.cache.CacheManager;

/**
 * @ClassName: MyMatcher
 * @Description: TODO(shiro密码配置)
 * @author HYK
 * @date 2017年3月9日 下午1:18:02
 */
public class MyMatcher extends HashedCredentialsMatcher {

	private Cache<String, AtomicInteger> passwordRetryCache;	//创建缓存的对象

	public MyMatcher(CacheManager cacheManager) {
		//赋予缓存对象,此处获取的是我们在ehcache.xml文件中配置,注意getCache("")获取的是xml中的name
		passwordRetryCache = cacheManager.getCache("passwordRetryCache");	
	}

	@Override
	public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
		String username = (String) token.getPrincipal();	//获取用户名

		AtomicInteger retryCount = passwordRetryCache.get(username);	//获取用户登录的次数

		if (retryCount == null) {	//如果用户未登陆过
			retryCount = new AtomicInteger(0);	//新建一个登录次数
			passwordRetryCache.put(username, retryCount);	//放入缓存中
		}

		if (retryCount.incrementAndGet() > 3) {	//如果用户登录次数超过三次(此处可根据需要自定义)
			throw new ExcessiveAttemptsException();	//抛出用户锁定异常类
		}

		boolean matches = super.doCredentialsMatch(token, info);	//判断用户是否可用,即是否为正确的账号密码
		
		if(matches){
			passwordRetryCache.remove(username);	//移除缓存中用户的登录次数
		}

		return matches;
	}

}

tips:引入类为org.apache.shiro下的;用户登录时的验证方法为doCredentialsMatch(token,info),此方法中将用户输入的令牌token和正确的令牌info进行验证。有兴趣的可以查看一下它的源码。在方法中它将info中的salt(盐)获取出来,然后根据我们在配置文件中的配置对用户输入的密码进行加密处理,然后对两者进行比较,比较一致则返回true,表示登录成功,否则返回false,表示登录失败。


7.登陆时我们需要捕获用户登录的异常:

@RequestMapping("/login")
	@ResponseBody
	public ResultInfo login(User user) {
		ResultInfo resultInfo = new ResultInfo();
		Subject subject = SecurityUtils.getSubject();
		UsernamePasswordToken token = new UsernamePasswordToken(user.getUser_name(), user.getPassword());
		try {
			subject.login(token);
			User userInfo = loginService.getUser(user.getUser_name());
			subject.getSession().setAttribute("user_id", userInfo.getUser_id());
			resultInfo.setCode(true); // 登录成功
		} catch (UnknownAccountException e) {
			resultInfo.setCode(false);
			resultInfo.setMsg("用户名/密码错误");
		} catch (IncorrectCredentialsException e) {
			resultInfo.setCode(false);
			resultInfo.setMsg("用户名/密码错误");
		} catch (ExcessiveAttemptsException e) {
			resultInfo.setCode(false);
			resultInfo.setMsg("登录失败多次,账户锁定10分钟");
		} catch (Exception e) {
			e.printStackTrace();
			resultInfo.setCode(false);
			resultInfo.setMsg("其他错误");
		}

		return resultInfo;
	}

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值