1.shiro的记住我与密码匹配都是基于cookie的,将信息存储在cookie中
2.基础配置 http://blog.csdn.net/zzhao114/article/details/55662585
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; }