上一章節我們知道了如何擴展自己的緩存機制,下面我們就學習下如何應用自己的自定義緩存,我們登錄都必須要寫一個realm,就是所謂的橋接器;
鑑於我們登錄都會把擁有的角色放到緩存,這樣都不用每次請求都要訪問一次數據庫,導致亞歷山大,當退出的時候又如何自動我們登錄時添加的緩存數據
下面帖個展示代碼
package com.silvery.security.shiro.realm;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import com.silvery.core.model.ViewResult;
import com.silvery.project.cms.model.Authority;
import com.silvery.project.cms.model.UserDetails;
import com.silvery.project.cms.service.AuthorityService;
import com.silvery.project.cms.service.UserDetailsService;
import com.silvery.project.cms.vo.AuthorityVo;
import com.silvery.project.cms.vo.UserDetailsVo;
import com.silvery.security.shiro.cache.SimpleMapCache;
import com.silvery.security.shiro.cache.extend.SimpleCacheManager;
/**
*
* 安全框架橋接器
*
* @author shadow
*
*/
public class SimpleUserRealm extends AuthorizingRealm {
private final static Logger log = LoggerFactory.getLogger(SimpleUserRealm.class);
@Autowired
private UserDetailsService userDetailsService;
@Autowired
private AuthorityService authorityService;
@Autowired
private SimpleCacheManager simpleCacheManager;
// 授權當前用戶信息
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
return getAuthorizationInfo(principals.getPrimaryPrincipal());
}
@SuppressWarnings("unchecked")
private SimpleAuthorizationInfo getAuthorizationInfo(Object principal) {
List<Authority> authorities = null;
// 獲取緩存,如失敗緩存則返回空角色集合
try {
authorities = (List<Authority>) simpleCacheManager.getCache(principal.toString()).get(principal);
} catch (Exception e) {
authorities = new ArrayList<Authority>();
log.error(e.getMessage());
}
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
// 添加角色到安全認證實體
for (Authority authority : authorities) {
authorizationInfo.addRole((authority.getName()));
}
return authorizationInfo;
}
// 用戶登錄認證
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken) throws AuthenticationException {
UsernamePasswordToken token = (UsernamePasswordToken) authcToken;
UserDetailsVo vo = new UserDetailsVo();
vo.setUsername(token.getUsername());
vo.setPassword(String.valueOf(token.getPassword()));
vo.setJcaptionCode(token.getHost());
// 使用用戶服務類接口查詢用戶是否存在
ViewResult viewResult = userDetailsService.login(vo);
if (viewResult.getSuccess()) {
UserDetails details = (UserDetails) viewResult.getValue();
// 加載用戶相應角色到緩存
loadUserAuthorityTocache(details);
// 返回安全框架認證信息
return new SimpleAuthenticationInfo(details.getUsername(), details.getPassword(), getName());
} else {
log.debug(new StringBuffer().append(token.getUsername()).append(" login failure at ").append(
new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())).append(", error message: ").append(
viewResult.getMessage()).toString());
// 失敗情況拋出異常並返回服務層相應錯誤信息
throw new AuthenticationException(viewResult.getMessage());
}
}
/**
* 加載用戶角色
*
* @param details
*/
private void loadUserAuthorityTocache(UserDetails details) {
// 使用當前用戶名作爲cache key
String cacheKey = details.getUsername();
AuthorityVo vo = new AuthorityVo();
vo.setUser_id(details.getId());
// 查詢用戶角色並存放到map
Map<Object, Object> map = new HashMap<Object, Object>();
map.put(cacheKey, authorityService.find4user(vo).getValue());
// 新建cache實例並放入緩存管理器
SimpleMapCache cache = new SimpleMapCache(cacheKey, map);
simpleCacheManager.createCache(details.getUsername(), cache);
}
/** 重寫退出時緩存處理方法 */
protected void doClearCache(PrincipalCollection principalcollection) {
Object principal = principalcollection.getPrimaryPrincipal();
simpleCacheManager.removeCache(principal.toString());
log.debug(new StringBuffer().append(principal).append(" on logout to remove the cache [").append(principal)
.append("]").toString());
}
}
1. 明顯看到登錄流程中,我們使用自己定義的緩存管理器,登錄認證成功後,然後加載出該用戶的相關角色,然後放到緩存中
2. 用戶認證的時候,是通過我們的自定義緩存管理器讀取資源,實現我們不用第二次查詢數據庫的需求
3. 如何在退出的自動清空相關緩存呢?可以看到我是重寫doClearCache方法,爲何要重寫?先看看下面帖上來的源碼
public abstract class CachingRealm
implements Realm, Nameable, CacheManagerAware, LogoutAware
{
public CachingRealm()
{
cachingEnabled = true;
name = (new StringBuilder()).append(getClass().getName()).append("_").append(INSTANCE_COUNT.getAndIncrement()).toString();
}
public CacheManager getCacheManager()
{
return cacheManager;
}
public void setCacheManager(CacheManager cacheManager)
{
this.cacheManager = cacheManager;
afterCacheManagerSet();
}
public boolean isCachingEnabled()
{
return cachingEnabled;
}
public void setCachingEnabled(boolean cachingEnabled)
{
this.cachingEnabled = cachingEnabled;
}
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
protected void afterCacheManagerSet()
{
}
public void onLogout(PrincipalCollection principals)
{
clearCache(principals);
}
protected void clearCache(PrincipalCollection principals)
{
if(!CollectionUtils.isEmpty(principals))
{
doClearCache(principals);
log.trace("Cleared cache entries for account with principals [{}]", principals);
}
}
protected void doClearCache(PrincipalCollection principalcollection)
{
}
protected Object getAvailablePrincipal(PrincipalCollection principals)
{
Object primary = null;
if(!CollectionUtils.isEmpty(principals))
{
Collection thisPrincipals = principals.fromRealm(getName());
if(!CollectionUtils.isEmpty(thisPrincipals))
primary = thisPrincipals.iterator().next();
else
primary = principals.getPrimaryPrincipal();
}
return primary;
}
private static final Logger log = LoggerFactory.getLogger(org/apache/shiro/realm/CachingRealm);
private static final AtomicInteger INSTANCE_COUNT = new AtomicInteger();
private String name;
private boolean cachingEnabled;
private CacheManager cacheManager;
}
明顯看到有個方法onLogout處理安全退出時的動作,然後試着重寫這個方法加入我們remove cache的動作,這樣就可以完美實現退出的時候自動清空自己的角色資源緩存,
但是有人會問,如果我不使用安全退出那資源會一直保留嗎?
這個是肯定的,但是當他下次登錄,會重新加載資源覆蓋之前的角色資源緩存
這次的試驗比較簡單,但是實用,相信對大家會有幫助,謝謝