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;
	}







發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章