目录
1、什么是MemoryConstrainedCacheManager
我们可以查看shiro提供给我们的信息了解这个 MemoryConstrainedCacheManager 的相关信息,在shiro提供的CacheManager接口下,默认提供了缓存抽象类 AbstractCacheManager.class ,还提供了一个实现类 MemoryConstrainedCacheManager 来实现缓存,但是这个缓存默认是关闭的,所以,在之前的篇幅中,我们每次登录,每次访问权限接口都会调用自定义realm中的权限认证和身份认证两个方法。
Memory Constrained Cache Manager 翻译过来就是保存在内存中的缓存管理器,由于运行在jvm环境下,也可以理解为使用jvm的内存保存数据的工具,适合在单实例使用。
- 在官方文档上有如下说法
基于内存的简单{@link CacheManager CacheManager}实现可在生产中使用
环境。它不会导致内存泄漏,因为它会生成{@link Cache Cache}
{@link SoftHashMap SoftHashMap}根据运行时环境的内存自动调整大小
限制和垃圾收集行为。
虽然创建的{@code Cache}实例是线程安全的,但它们不提供任何企业级功能,例如
缓存一致性,乐观锁定,故障转移或其他类似功能。有关更多企业功能,请考虑
使用由企业级缓存产品支持的不同{@code CacheManager}实现(EhCache,
TerraCotta,Coherence,GigaSpaces等等)
2、为什么要使用缓存
在之前的配置中,并没有使用缓存,我们可以看到每次登录或者访问权限接口时,总会频繁的调用我们自定义realm的身份认证和权限认证的方法,尤其是权限认证!频繁的对数据库进行访问,占用很多数据库资源,这是我们不想看到的,使用缓存可以将一些不会经常变动的值存下来,需要时直接在缓存中取数据对比即可,无需访问数据库,减少数据库资源的占用,提高系统性能等!
3、如何使用
- ShiroConfig类中添加缓存管理器
/**
* 缓存管理器
* @return
*/
@Bean
public CacheManager cacheManager() {
MemoryConstrainedCacheManager mccm = new MemoryConstrainedCacheManager();
return mccm;
}
- 授权管理器交给securityManager
/**
* 注入 securityManager
*/
@Bean
public SecurityManager securityManager() {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(myShiroRealm());
securityManager.setRememberMeManager(rememberMeManager());
securityManager.setCacheManager(cacheManager());//配置缓存管理器
return securityManager;
}
- 自定义realm中开启缓存
/**
* 自定义身份认证 realm;
* <p>
* 必须写这个类,并加上 @Bean 注解,目的是注入 MyShiroRealm, 否则会影响 MyShiroRealm类 中其他类的依赖注入
*/
@Bean
public MyShiroRealm myShiroRealm() {
MyShiroRealm myShiroRealm = new MyShiroRealm();
// 设置密码比较器
myShiroRealm.setCredentialsMatcher(CredentialsMatcher());
// 启用身份验证缓存,即缓存AuthenticationInfo信息,默认false
myShiroRealm.setAuthenticationCachingEnabled(true);
// 启用授权缓存,即缓存AuthorizationInfo信息,默认false,一旦配置了缓存管理器,授权缓存默认开启
myShiroRealm.setAuthorizationCachingEnabled(true);
return myShiroRealm;
}
4、如何清理缓存
4.1、调用logout方法
我们发现调用logout方法可以同时清除两种缓存,但是此方法只能清除当前用户,并且会强制退出,实用性不强。
Subject subject = SecurityUtils.getSubject();
// 登出
subject.logout();
4.2、主动清理缓存
4.2.1、部分新增缓存代码
可以看出,我们使用的缓存保存的key是采用 principals 属性,因此我们清除的时候只需要传入用户名即可!
//缓存代码
class AuthorizingRealm{
.........................
if (info == null) {
// Call template method if the info was not found in a cache
info = doGetAuthorizationInfo(principals);
// If the info is not null and the cache has been created, then cache the authorization info.
if (info != null && cache != null) {
if (log.isTraceEnabled()) {
log.trace("Caching authorization info for principals: [" + principals + "].");
}
Object key = getAuthorizationCacheKey(principals);
cache.put(key, info);
}
}
4.2.2、配置主动缓存清理
- 在 MyShiroRealm 类中新增下列方法
/**
* 重写方法,清除当前用户的的 授权缓存
* @param principals
*/
public void clearCachedAuthorizationInfo() {
super.clearCachedAuthorizationInfo(SecurityUtils.getSubject().getPrincipals());
}
/**
* 重写方法,清除当前用户的 认证缓存
* @param principals
*/
public void clearCachedAuthenticationInfo() {
super.clearCachedAuthenticationInfo(SecurityUtils.getSubject().getPrincipals());
}
/**
* 清除某个用户认证和授权缓存
*/
@Override
public void clearCache(PrincipalCollection principals) {
super.clearCache(principals);
}
/**
* 自定义方法:清除所有 授权缓存
*/
public void clearAllCachedAuthorizationInfo() {
getAuthorizationCache().clear();
}
/**
* 自定义方法:清除所有 认证缓存
*/
public void clearAllCachedAuthenticationInfo() {
getAuthenticationCache().clear();
}
/**
* 自定义方法:清除所有的 认证缓存 和 授权缓存
*/
public void clearAllCache() {
clearAllCachedAuthenticationInfo();
clearAllCachedAuthorizationInfo();
}
- ShiroController 新增接口
@RequestMapping("/clear")
public ReturnMap clear() {
RealmSecurityManager rsm = (RealmSecurityManager)SecurityUtils.getSecurityManager();
MyShiroRealm realm = (MyShiroRealm)rsm.getRealms().iterator().next();
realm.clearCachedAuthenticationInfo();
realm.clearCachedAuthorizationInfo();
System.out.println("清除成功");
return new ReturnMap().success().data("清除成功");
}
- 页面新增清除接口按钮
5、测试
- 我们登录了3次,并且访问了很多次权限接口的情况下,下图红色部分为进入认证器的次数,也可以理解为访问数据库次数,只有两次,测试成功!
- 接着上面的测试继续,我们点击clear接口,并且再进行登录和接口访问,可以看到,每次清除完缓存之后,下次访问必定会再次访问数据库,把数据写入缓存,清除缓存测试成功!