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