项目源码:https://github.com/weimingge14/Shiro-project
演示地址:http://liweiblog.duapp.com/Shiro-project/login
用户名:admin,密码:123456
用户名:liwei,密码:123456
用户名:huzhenyu,密码:123456

我们在验证用户具有某些权限的时候,常常会调用自定义 Realm 的授权方法,这个授权方法里面常常要进行数据库查询的。在一个用户权限不会经常变更的情况下,如果不对授权的方法增加缓存,每次检查权限都去查询数据库是很浪费资源的。

步骤1:添加 ehcache 依赖

<!-- 添加 shiro-ehcache 接口依赖-->
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-ehcache</artifactId>
    <version>${shiro.version}</version>
</dependency>

<!-- 添加 ehcache 实现 -->
<dependency>
    <groupId>net.sf.ehcache</groupId>
    <artifactId>ehcache</artifactId>
    <version>2.10.2.2.21</version>
</dependency>

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context-support</artifactId>
    <version>${spring.version}</version>
</dependency>

步骤2:在 Spring 的 Shiro 配置中将自定义 Realm 的部分配置为可以进行缓存的

<!-- 声明一个自定义的 Realm -->
<bean id="myRealm" class="com.liwei.shiro.realm.MyRealm">
    <!-- 将上面声明的密码匹配器注入到自定义 Realm 的属性中去 -->
    <property name="credentialsMatcher" ref="credentialsMatcher"/>
    <!-- 将自定义的权限匹配器注入到自定义 Realm 中 -->
    <property name="permissionResolver" ref="permissionResolver"/>

    <!-- 配置缓存相关 -->
    <!-- 启用缓存 -->
    <property name="cachingEnabled" value="true"/>
    <!-- 开启认证缓存-->
    <property name="authenticationCachingEnabled" value="true"/>
    <!-- 指定认证缓存的名字(与 ehcache.xml 中声明的相同) -->
    <property name="authenticationCacheName" value="shiro-authenticationCache"/>
    <!--开启授权缓存-->
    <property name="authorizationCachingEnabled" value="true"/>
    <!-- 指定授权缓存的名字(与 ehcache.xml 中声明的相同) -->
    <property name="authorizationCacheName" value="shiro-authorizationCache"/>

</bean>

步骤3:重写自定义 Realm 部分的清除缓存的方法

@Override
protected void clearCachedAuthenticationInfo(PrincipalCollection principals) {
    Cache c = getAuthenticationCache();
    logger.info("清除【认证】缓存之前");
    for(Object o : c.keys()){
        logger.info( o + " , " + c.get(o));
    }
    super.clearCachedAuthenticationInfo(principals);
    logger.info("调用父类清除【认证】缓存之后");
    for(Object o : c.keys()){
        logger.info( o + " , " + c.get(o));
    }

    // 添加下面的代码清空【认证】的缓存
    User user = (User) principals.getPrimaryPrincipal();
    SimplePrincipalCollection spc = new SimplePrincipalCollection(user.getUsername(),getName());
    super.clearCachedAuthenticationInfo(spc);
    logger.info("添加了代码清除【认证】缓存之后");
    int cacheSize = c.keys().size();
    logger.info("【认证】缓存的大小:" + c.keys().size());
    if (cacheSize == 0){
        logger.info("说明【认证】缓存被清空了。");
    }
}

@Override
protected void clearCachedAuthorizationInfo(PrincipalCollection principals) {
    logger.info("清除【授权】缓存之前");
    Cache c = getAuthorizationCache();
    for(Object o : c.keys()){
        logger.info( o + " , " + c.get(o));
    }
    super.clearCachedAuthorizationInfo(principals);
    logger.info("清除【授权】缓存之后");
    int cacheSize = c.keys().size();
    logger.info("【授权】缓存的大小:" + cacheSize);

    for(Object o : c.keys()){
        logger.info( o + " , " + c.get(o));
    }
    if(cacheSize == 0){
        logger.info("说明【授权】缓存被清空了。");
    }

}

此时可以打开 MyBatis 的 SQL 打印的配置。

log4j.logger.com.liwei.shiro.dao=TRACE
log4j.logger.java.sql.ResultSet=INFO
log4j.logger.org.apache=INFO
log4j.logger.java.sql.Connection=INFO
log4j.logger.java.sql.Statement=INFO
log4j.logger.java.sql.PreparedStatement=INFO

其中 log4j.logger.com.liwei.shiro.dao 是 MyBatis 接口文件所在的包名。