天天看点

shiro记住我与密码匹配次数的配置(md5加密)

简介

1.shiro的记住我与密码匹配都是基于cookie的,将信息存储在cookie中

2.基础配置 http://blog.csdn.net/zzhao114/article/details/55662585

remenberme功能

1.首先在shiro的配置文件中添加

<bean id="rememberMeCookie" class="org.apache.shiro.web.servlet.SimpleCookie">
		<constructor-arg value="rememberMe" />
		<property name="httpOnly" value="true" />
		<property name="maxAge" value="604800" /><!-- 7天 -->
	</bean>
	<!-- rememberMe管理器 -->
	<bean id="rememberMeManager" class="org.apache.shiro.web.mgt.CookieRememberMeManager">
		<property name="cipherKey"
			value="#{T(org.apache.shiro.codec.Base64).decode('4AvVhmFLUs0KTA3Kprsdag==')}" />
		<property name="cookie" ref="rememberMeCookie" />
	</bean>
           

2.将配置的管理器加载到securityManager

<!-- 安全管理器 -->
	<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
		<property name="realm" ref="userRealm" />
		<!-- 使用下面配置的缓存管理器 -->
		<property name="cacheManager" ref="cacheManager" />
		<property name="sessionManager" ref="sessionManager" />
		<property name="rememberMeManager" ref="rememberMeManager" />
	</bean>
           

3.登入时,将前台获取的数据(true or false)加入到验证的token中

token.setRememberMe(userValidate.getRememberme());
           

密码匹配的配置(使用md5加密)

1.额外需要的jar包

<!-- 密码工具 -->
		<dependency>
			<groupId>com.google.guava</groupId>
			<artifactId>guava</artifactId>
			<version>r07</version>
		</dependency>
		<!-- 密码次数锁定需要的jar -->
		<dependency>
			<groupId>net.sf.ehcache</groupId>
			<artifactId>ehcache-core</artifactId>
			<version>2.4.8</version>
		</dependency>
		<!-- shiro与ehcache整合需要的jar -->
		<dependency>
			<groupId>org.apache.shiro</groupId>
			<artifactId>shiro-ehcache</artifactId>
			<version>${shiro.version}</version>
		</dependency>
           

2.配置密码匹配器,即配置自定义的匹配次数的类

<!-- 凭证匹配器 -->
	<bean id="passwordMatcher" class="com.shiro.RetryLimitHashedCredentialsMatcher">
		<constructor-arg ref="cacheManager" />
		<property name="hashAlgorithmName" value="md5" />
		<property name="hashIterations" value="3" />
		<property name="storedCredentialsHexEncoded" value="true" />
	</bean>
           

RetryLimitHashedCredentialsMatcher.java

package com.shiro;

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;
import java.util.concurrent.atomic.AtomicInteger;

public class RetryLimitHashedCredentialsMatcher extends
		HashedCredentialsMatcher {

	private Cache<String, AtomicInteger> passwordRetryCache;

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

	@Override
	public boolean doCredentialsMatch(AuthenticationToken token,
			AuthenticationInfo info) {
		String username = (String) token.getPrincipal();
		// retry count + 1
		AtomicInteger retryCount = passwordRetryCache.get(username);
		if (retryCount == null) {
			retryCount = new AtomicInteger(0);
			passwordRetryCache.put(username, retryCount);
		}
		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;
	}
}
           

3.配置缓存管理器,用来存储错误次数等

<!-- 缓存管理器开始 -->
	<bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
		<property name="cacheManager" ref="ehCacheManager" />
	</bean>
	<bean id="ehCacheManager"
		class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
		<property name="configLocation" value="classpath:config/shiro-ehcache.xml" />
		<property name="shared" value="true"></property>
	</bean>
           

shiro-ehcache.xml

<?xml version="1.0" encoding="UTF-8"?>
<ehcache name="shirocache">

	<diskStore path="java.io.tmpdir" />

	<!-- 登录记录缓存 锁定10分钟 -->
	<cache name="passwordRetryCache" eternal="false"
		timeToIdleSeconds="3600" timeToLiveSeconds="0" overflowToDisk="false"
		statistics="true">
	</cache>

	<cache name="authorizationCache" eternal="false"
		timeToIdleSeconds="3600" timeToLiveSeconds="0" overflowToDisk="false"
		statistics="true">
	</cache>

	<cache name="authenticationCache" eternal="false"
		timeToIdleSeconds="3600" timeToLiveSeconds="0" overflowToDisk="false"
		statistics="true">
	</cache>

	<cache name="shiro-activeSessionCache" eternal="false"
		timeToIdleSeconds="3600" timeToLiveSeconds="0" overflowToDisk="false"
		statistics="true">
	</cache>

</ehcache>
           

spring-shiro.xml的完整配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://www.springframework.org/schema/beans" xmlns:util="http://www.springframework.org/schema/util"
	xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p"
	xmlns:tx="http://www.springframework.org/schema/tx" xmlns:mvc="http://www.springframework.org/schema/mvc"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
	http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/tx
	http://www.springframework.org/schema/tx/spring-tx-4.0.xsd http://www.springframework.org/schema/context
	http://www.springframework.org/schema/context/spring-context-4.0.xsd http://www.springframework.org/schema/mvc
	http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/aop
	http://www.springframework.org/schema/aop/spring-aop-4.0.xsd http://www.springframework.org/schema/util 
	http://www.springframework.org/schema/util/spring-util.xsd">

	<context:component-scan base-package="com.shiro" />
	<!-- 配置shiro的过滤器工厂类,id- shiroFilter要和我们在web.xml中配置的过滤器一致 -->
	<bean id="shiroFilter" class="com.shiro.ShiroPermissionFactory">
		<!-- 调用我们配置的权限管理器 -->
		<property name="securityManager" ref="securityManager" />
		<!-- 配置我们的登录请求地址 -->
		<property name="loginUrl" value="/view/login.html" />
		<!-- 配置我们在登录页登录成功后的跳转地址,如果你访问的是非/login地址,则跳到您访问的地址 -->
		<property name="successUrl" value="/index.html" />
		<!-- 如果您请求的资源不再您的权限范围,则跳转到/403请求地址 -->
		<property name="unauthorizedUrl" value="/view/403.html" />
		<property name="filters">
			<util:map>
				<entry key="logout" value-ref="logoutFilter" />
			</util:map>
		</property>
		<!-- 权限配置 -->
		<property name="filterChainDefinitions">
			<value>
				/uploadImg=authc
				/login=anon
				/relogin=anon
				/getAuthImg=anon
				/css/**=anon
				/datas/**=anon
				/images/**=anon
				/js/**=anon
				/lay/**=anon
				/plugins/**=anon
				/logout=logout
			</value>
		</property>
	</bean>
	<bean id="logoutFilter" class="org.apache.shiro.web.filter.authc.LogoutFilter">
		<property name="redirectUrl" value="/view/login.html" />
	</bean>

	<!-- 凭证匹配器 -->
	<bean id="passwordMatcher" class="com.shiro.RetryLimitHashedCredentialsMatcher">
		<constructor-arg ref="cacheManager" />
		<property name="hashAlgorithmName" value="md5" />
		<property name="hashIterations" value="3" />
		<property name="storedCredentialsHexEncoded" value="true" />
	</bean>

	<!-- 会话Cookie模板 关闭浏览器立即失效 -->
	<bean id="sessionIdCookie" class="org.apache.shiro.web.servlet.SimpleCookie">
		<constructor-arg value="sid" />
		<property name="httpOnly" value="true" />
		<property name="maxAge" value="-1" />
	</bean>
	<bean id="rememberMeCookie" class="org.apache.shiro.web.servlet.SimpleCookie">
		<constructor-arg value="rememberMe" />
		<property name="httpOnly" value="true" />
		<property name="maxAge" value="604800" /><!-- 7天 -->
	</bean>
	<!-- rememberMe管理器 -->
	<bean id="rememberMeManager" class="org.apache.shiro.web.mgt.CookieRememberMeManager">
		<property name="cipherKey"
			value="#{T(org.apache.shiro.codec.Base64).decode('4AvVhmFLUs0KTA3Kprsdag==')}" />
		<property name="cookie" ref="rememberMeCookie" />
	</bean>

	<!-- 会话ID生成器 -->
	<bean id="sessionIdGenerator"
		class="org.apache.shiro.session.mgt.eis.JavaUuidSessionIdGenerator" />
	<!-- 会话DAO -->
	<bean id="sessionDAO"
		class="org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO">
		<property name="sessionIdGenerator" ref="sessionIdGenerator" />
	</bean>
	<!-- 会话验证调度器,每30分钟执行一次验证 ,设定会话超时及保存 -->
	<bean name="sessionValidationScheduler"
		class="org.apache.shiro.session.mgt.ExecutorServiceSessionValidationScheduler">
		<property name="interval" value="1800000" />
		<property name="sessionManager" ref="sessionManager" />
	</bean>
	<!-- 会话管理器 -->
	<bean id="sessionManager"
		class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
		<!-- 全局会话超时时间(单位毫秒),默认30分钟 -->
		<property name="globalSessionTimeout" value="1800000" />
		<property name="deleteInvalidSessions" value="true" />
		<property name="sessionValidationSchedulerEnabled" value="true" />
		<property name="sessionValidationScheduler" ref="sessionValidationScheduler" />
		<property name="sessionDAO" ref="sessionDAO" />
		<property name="sessionIdCookieEnabled" value="true" />
		<property name="sessionIdCookie" ref="sessionIdCookie" />
	</bean>

	<!-- 安全管理器 -->
	<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
		<property name="realm" ref="userRealm" />
		<!-- 使用下面配置的缓存管理器 -->
		<property name="cacheManager" ref="cacheManager" />
		<property name="sessionManager" ref="sessionManager" />
		<property name="rememberMeManager" ref="rememberMeManager" />
	</bean>
	<!-- 相当于调用SecurityUtils.setSecurityManager(securityManager) -->
	<bean
		class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
		<property name="staticMethod"
			value="org.apache.shiro.SecurityUtils.setSecurityManager" />
		<property name="arguments" ref="securityManager" />
	</bean>

	<!-- 注册自定义的Realm,并把密码匹配器注入,使用注解的方式自动注解会无法正确匹配密码 -->
	<bean id="userRealm" class="com.shiro.UserRealm">
		<property name="credentialsMatcher" ref="passwordMatcher" />
		<property name="cachingEnabled" value="false" />
	</bean>

	<!-- 缓存管理器开始 -->
	<bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
		<property name="cacheManager" ref="ehCacheManager" />
	</bean>
	<bean id="ehCacheManager"
		class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
		<property name="configLocation" value="classpath:config/shiro-ehcache.xml" />
		<property name="shared" value="true"></property>
	</bean>
	<!-- 保证实现了Shiro内部lifecycle函数的bean执行 -->
	<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor" />
</beans> 
           

使用MD5加密的添加方法

public User createUser(User user) {
		user.setImg(IMG);
		user = EndecryptUtils.md5Password(user);
		usermapper.adduser(user);
		return user;
	}
           

其中用的EndecryptUtils.java类

package com.util;

import com.pojo.User;
import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import org.apache.shiro.codec.Base64;
import org.apache.shiro.codec.Hex;
import org.apache.shiro.crypto.AesCipherService;
import org.apache.shiro.crypto.SecureRandomNumberGenerator;
import org.apache.shiro.crypto.hash.Md5Hash;
import java.security.Key;

/**
 * 备注: shiro进行加密解密的工具类封装
 */
public final class EndecryptUtils {

	//需要与shiro配置文件中一致
	private static final int HASH_ITERATIONS = 3;

	/**
	 * base64进制加密
	 * 
	 * @param password
	 * @return
	 */
	public static String encrytBase64(String password) {
		Preconditions.checkArgument(!Strings.isNullOrEmpty(password), "不能为空");
		byte[] bytes = password.getBytes();
		return Base64.encodeToString(bytes);
	}

	/**
	 * base64进制解密
	 * 
	 * @param cipherText
	 * @return
	 */
	public static String decryptBase64(String cipherText) {
		Preconditions.checkArgument(!Strings.isNullOrEmpty(cipherText), "消息摘要不能为空");
		return Base64.decodeToString(cipherText);
	}

	/**
	 * 16进制加密
	 * 
	 * @param password
	 * @return
	 */
	public static String encrytHex(String password) {
		Preconditions.checkArgument(!Strings.isNullOrEmpty(password), "不能为空");
		byte[] bytes = password.getBytes();
		return Hex.encodeToString(bytes);
	}

	/**
	 * 16进制解密
	 * 
	 * @param cipherText
	 * @return
	 */
	public static String decryptHex(String cipherText) {
		Preconditions.checkArgument(!Strings.isNullOrEmpty(cipherText), "消息摘要不能为空");
		return new String(Hex.decode(cipherText));
	}

	public static String generateKey() {
		AesCipherService aesCipherService = new AesCipherService();
		Key key = aesCipherService.generateNewKey();
		return Base64.encodeToString(key.getEncoded());
	}

	/**
	 * 对密码进行md5加密,并返回密文和salt,包含在User对象中
	 * 
	 * @param username
	 *            用户名
	 * @param password
	 *            密码
	 * @return 密文和salt
	 */
	public static User md5Password(User user) {
		Preconditions.checkArgument(!Strings.isNullOrEmpty(user.getName()), "username不能为空");
		Preconditions.checkArgument(!Strings.isNullOrEmpty(user.getPassword()), "password不能为空");
		SecureRandomNumberGenerator secureRandomNumberGenerator = new SecureRandomNumberGenerator();
		String salt = secureRandomNumberGenerator.nextBytes().toHex();
		String password_cipherText = new Md5Hash(user.getPassword(), user.getName() + salt, HASH_ITERATIONS).toHex();
		user.setPassword(password_cipherText);
		user.setSalt(salt);
		return user;
	}
}
           

自定义的UserRealm.java中的验证方法

protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {

		String username = (String) token.getPrincipal();
		User user = userService.findByUsername(username);

		if (user == null) {
			throw new UnknownAccountException();// 没找到帐号
		}

		// 判断帐号是否锁定
		if (Boolean.TRUE.equals(user.isLocked())) {
			// 抛出 帐号锁定异常
			throw new LockedAccountException();
		}

		SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(user.getName(), user.getPassword(),
				ByteSource.Util.bytes(user.getName() + user.getSalt()), getName());

		return authenticationInfo;
	}