目錄
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接口,並且再進行登錄和接口訪問,可以看到,每次清除完緩存之後,下次訪問必定會再次訪問數據庫,把數據寫入緩存,清除緩存測試成功!