这个是在ssm的基础上再去整合shiro和ehcache的,整合ehcache主要是为了减少后台shiro拦截的次数,因为如果我们不使用缓存的话,后台shiro的认证和授权的拦截器就会反复的进行拦截,导致系统的运行效率不高,因此使用缓存是一种很好的解决的方法,下面我们看看如何整合ehcache。
1、加入jar包pom.xml
在这之前,我们先加入shiro和ehcache的相关jar包
<!-- shiro -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<exclusions>
<exclusion>
<artifactId>slf4j-api</artifactId>
<groupId>org.slf4j</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-ehcache</artifactId>
<exclusions>
<exclusion>
<artifactId>slf4j-api</artifactId>
<groupId>org.slf4j</groupId>
</exclusion>
</exclusions>
</dependency>
2、配置ehcache.xml配置文件
在整合之前,我们需要配置一下ehcache的一些参数,设置好ehcache的环境。
<?xml version="1.0" encoding="UTF-8"?>
<ehcache updateCheck="false" name="shiroCache">
<diskStore path="C:\shiro\ehcache" />
<!-- <diskStore path="java.io.tmpdir"/> -->
<!--
eternal:缓存中对象是否为永久的,如果是,超时设置将被忽略,对象从不过期。
maxElementsInMemory:缓存中允许创建的最大对象数
overflowToDisk:内存不足时,是否启用磁盘缓存。
timeToIdleSeconds:缓存数据的钝化时间,也就是在一个元素消亡之前, 两次访问时间的最大时间间隔值,这只能在元素不是永久驻留时有效,如果该值是 0 就意味着元素可以停顿无穷长的时间。
timeToLiveSeconds:缓存数据的生存时间,也就是一个元素从构建到消亡的最大时间间隔值,这只能在元素不是永久驻留时有效,如果该值是0就意味着元素可以停顿无穷长的时间。
memoryStoreEvictionPolicy:缓存满了之后的淘汰算法。
diskPersistent:设定在虚拟机重启时是否进行磁盘存储,默认为false
diskExpiryThreadIntervalSeconds: 属性可以设置该线程执行的间隔时间(默认是120秒,不能太小
1 FIFO,先进先出
2 LFU,最少被使用,缓存的元素有一个hit属性,hit值最小的将会被清出缓存。
3 LRU,最近最少使用的,缓存的元素有一个时间戳,当缓存容量满了,而又需要腾出地方来缓存新的元素的时候,那么现有缓存元素中时间戳离当前时间最远的元素将被清出缓存。
-->
<defaultCache
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
overflowToDisk="false"
diskPersistent="false"
diskExpiryThreadIntervalSeconds="120"
/>
<cache name="activeSessionCache"
maxElementsInMemory="10000"
eternal="true"
overflowToDisk="false"
diskPersistent="true"
diskExpiryThreadIntervalSeconds="600"/>
<cache name="shiro.authorizationCache"
maxElementsInMemory="100"
eternal="false"
timeToLiveSeconds="600"
overflowToDisk="false"/>
</ehcache>
3、整合spring
整合spring其实很简单的,以前总觉得是一个很复杂的事情一样,每次什么都要整合spring,其实spring就是一个容器,再说的直白点,就是一个map类似的容器,然后通过对容器的操作来实现,因此,如果我们需要让spring来管理ehcache的话,我们只是需要将ehcache的配置文件交给spring来管理即可。
下面就是spring的配置文件
applicationContext-ehcache.xml
的ehcache的配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:cache="http://www.springframework.org/schema/cache"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/cache
http://www.springframework.org/schema/cache/spring-cache.xsd ">
<!-- 安全管理器 -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="userRealm" />
<property name="cacheManager" ref="cacheManager"/>
</bean>
<!-- 缓存管理器 -->
<bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
<property name="cacheManagerConfigFile" value="classpath:shiro-ehcache.xml"/>
</bean>
</beans>
4、缓冲清空
当用户权限修改后,用户再次登陆shiro会自动调用realm从数据库获取权限数据,如果在修改权限后想立即清除缓存则可以调用realm的clearCache方法清除缓存。
realm中定义clearCached方法:
@Component
public class ShiroDBRealm extends AuthorizingRealm {<!-- -->
/**
*
* @Description: 权限修改生效后,立即刷新清空缓存,则可以实现用户不退出生效新的权限
*
* @author sihai
* @date 2017年9月29日 下午9:34:07
*/
public void clearCache() {
PrincipalCollection principals = SecurityUtils.getSubject().getPrincipals();
super.clearCache(principals);
}
}
5、测试是否整合成功
@Component
public class ShiroDBRealm extends AuthorizingRealm {<!-- -->
@Autowired
private UserService userService;
public ShiroDBRealm(CacheManager cacheManager) {
super(cacheManager);
}
/**
* 用于登录认证
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
// 1. 从token中获取用户在表单中输入的域,即 用户名 以及 密码,以此来和数据库中的用户真实数据进行匹配
UsernamePasswordToken userToken = (UsernamePasswordToken)token;
String username = userToken.getUsername(); // 用户的登录名
String password = String.valueOf(userToken.getPassword()); // 用户的密码
// 2. 根据用户输入的用户名和数据库进行匹配,查询数据库中的用户,如果查询不到,则返回一个null
SysUser user = userService.queryUserByUsername(username);
// 3. 判断数据库中查询出来的用户是否存在,不存在代表用户名密码错误;如果存在,则返回 AuthenticationInfo
if (user == null) {
return null;
}
String dbPassword = user.getPassword();
String dbSalt = user.getAuthSalt();
String userPassword = ShiroPasswordUtil.getShiroPassword(password, dbSalt, 2);
if (!userPassword.equals(dbPassword)) {
// 抛出一个异常,密码不正确
throw new IncorrectCredentialsException();
}
ActiveUser activeUser = new ActiveUser();
activeUser.setUserId(user.getId());
activeUser.setUsername(user.getUsername());
// 4. 返回AuthenticationInfo
// 参数意义:
// Object principal: 用户对象,可以使一个对象类,或者一个字符串,存与session中
// Object credentials:密码
// 题外话:我们目前直接根据用户名密码来查询,所以这里直接放用户输入的密码即可;但是,也可疑直接用用户名来查询数据库中的用户,再然后进行密码的比对,如此则此处应该填写用户输入的密码
// String realmName:当前我们自定义realm的名称
AuthenticationInfo info = new SimpleAuthenticationInfo(activeUser, password, getName());
return info;
}
/**
* 用于授权鉴权
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
// 从principals中获取当前用户
ActiveUser sessionUser = (ActiveUser)principals.getPrimaryPrincipal();
String userid = sessionUser.getUserId();
// 模拟从数据库中获取用户的权限(资源权限字符串)
List<SysPermission> permissionList = null;
try {
permissionList = userService.findPermissionListByUserId(userid);
} catch (Exception e) {
e.printStackTrace();
}
List<String> percodeList = new ArrayList<String>();
for (SysPermission p : permissionList) {
percodeList.add(p.getPercode());
}
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
simpleAuthorizationInfo.addStringPermissions(percodeList);
return simpleAuthorizationInfo;
}
}
上面登录认证和授权的代码,当我们没有加入ehcache缓存的时候,我们前台每次授权认证的时候,我们都会访问这段代码,如果当我们只会第一次访问,后面不再访问的时候,说明整合成功了。