您的位置:首页 > 编程语言 > Java开发

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

2014-09-24 15:30 363 查看
        上一章节我们知道了如何扩展自己的缓存机制,下面我们就学习下如何应用自己的自定义缓存,我们登录都必须要写一个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的动作,这样就可以完美实现退出的时候自动清空自己的角色资源缓存,

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

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

这次的试验比较简单,但是实用,相信对大家会有帮助,谢谢
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  shiro 开源 Java 框架 安全
相关文章推荐