shiro教程:整合ehcache缓存

这个是在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缓存的时候,我们前台每次授权认证的时候,我们都会访问这段代码,如果当我们只会第一次访问,后面不再访问的时候,说明整合成功了。

猜你喜欢

转载自blog.csdn.net/sihai12345/article/details/81168627