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