shiro安全框架扩展教程--如何扩展realm桥接器并退出自动清空角色资源缓存

来源:互联网 发布:beyong compare mac 编辑:程序博客网 时间:2024/05/14 06:02

        上一章节我们知道了如何扩展自己的缓存机制,下面我们就学习下如何应用自己的自定义缓存,我们登录都必须要写一个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);@Autowiredprivate UserDetailsService userDetailsService;@Autowiredprivate AuthorityService authorityService;@Autowiredprivate 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 keyString cacheKey = details.getUsername();AuthorityVo vo = new AuthorityVo();vo.setUser_id(details.getId());// 查询用户角色并存放到mapMap<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的动作,这样就可以完美实现退出的时候自动清空自己的角色资源缓存,


但是有人会问,如果我不使用安全退出那资源会一直保留吗?


这个是肯定的,但是当他下次登录,会重新加载资源覆盖之前的角色资源缓存


这次的试验比较简单,但是实用,相信对大家会有帮助,谢谢


2 0
原创粉丝点击