上一章节我们知道了如何扩展自己的缓存机制,下面我们就学习下如何应用自己的自定义缓存,我们登录都必须要写一个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; }
但是有人会问,如果我不使用安全退出那资源会一直保留吗?
这个是肯定的,但是当他下次登录,会重新加载资源覆盖之前的角色资源缓存
这次的试验比较简单,但是实用,相信对大家会有帮助,谢谢