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

简介:         上一章节我们知道了如何扩展自己的缓存机制,下面我们就学习下如何应用自己的自定义缓存,我们登录都必须要写一个realm,就是所谓的桥接器;鉴于我们登录都会把拥有的角色放到缓存...

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


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


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


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


目录
相关文章
|
6月前
|
缓存 Java 应用服务中间件
Spring Boot配置优化:Tomcat+数据库+缓存+日志,全场景教程
本文详解Spring Boot十大核心配置优化技巧,涵盖Tomcat连接池、数据库连接池、Jackson时区、日志管理、缓存策略、异步线程池等关键配置,结合代码示例与通俗解释,助你轻松掌握高并发场景下的性能调优方法,适用于实际项目落地。
1100 5
|
8月前
|
存储 缓存 NoSQL
Spring Cache缓存框架
Spring Cache是Spring体系下的标准化缓存框架,支持多种缓存(如Redis、EhCache、Caffeine),可独立或组合使用。其优势包括平滑迁移、注解与编程两种使用方式,以及高度解耦和灵活管理。通过动态代理实现缓存操作,适用于不同业务场景。
640 0
|
缓存 NoSQL Java
什么是缓存?如何在 Spring Boot 中使用缓存框架
什么是缓存?如何在 Spring Boot 中使用缓存框架
943 0
|
缓存 监控 前端开发
在资源加载优化中,如何利用浏览器缓存提升性能?
通过以上这些方法,可以有效地利用浏览器缓存来提升资源加载的性能,减少网络请求次数,提高用户体验和应用的响应速度。同时,需要根据具体的应用场景和资源特点进行灵活调整和优化,以达到最佳的效果。此外,随着技术的不断发展和变化,还需要持续关注和学习新的缓存优化方法和策略。
608 159
|
缓存 NoSQL Java
Redis应用—8.相关的缓存框架
本文介绍了Ehcache和Guava Cache两个缓存框架及其使用方法,以及如何自定义缓存。主要内容包括:Ehcache缓存框架、Guava Cache缓存框架、自定义缓存。总结:Ehcache适合用作本地缓存或与Redis结合使用,Guava Cache则提供了更灵活的缓存管理和更高的并发性能。自定义缓存可以根据具体需求选择不同的数据结构和引用类型来实现特定的缓存策略。
803 16
Redis应用—8.相关的缓存框架
|
存储 缓存 监控
Linux缓存管理:如何安全地清理系统缓存
在Linux系统中,内存管理至关重要。本文详细介绍了如何安全地清理系统缓存,特别是通过使用`/proc/sys/vm/drop_caches`接口。内容包括清理缓存的原因、步骤、注意事项和最佳实践,帮助你在必要时优化系统性能。
1351 78
|
存储 缓存 自然语言处理
SCOPE:面向大语言模型长序列生成的双阶段KV缓存优化框架
KV缓存是大语言模型(LLM)处理长文本的关键性能瓶颈,现有研究多聚焦于预填充阶段优化,忽视了解码阶段的重要性。本文提出SCOPE框架,通过分离预填充与解码阶段的KV缓存策略,实现高效管理。SCOPE保留预填充阶段的关键信息,并在解码阶段引入滑动窗口等策略,确保重要特征的有效选取。实验表明,SCOPE仅用35%原始内存即可达到接近完整缓存的性能水平,显著提升了长文本生成任务的效率和准确性。
755 3
SCOPE:面向大语言模型长序列生成的双阶段KV缓存优化框架
|
SQL 缓存 Java
【详细实用のMyBatis教程】获取参数值和结果的各种情况、自定义映射、动态SQL、多级缓存、逆向工程、分页插件
本文详细介绍了MyBatis的各种常见用法MyBatis多级缓存、逆向工程、分页插件 包括获取参数值和结果的各种情况、自定义映射resultMap、动态SQL
【详细实用のMyBatis教程】获取参数值和结果的各种情况、自定义映射、动态SQL、多级缓存、逆向工程、分页插件
|
缓存 Java 开发工具
Spring是如何解决循环依赖的?从底层源码入手,详细解读Spring框架的三级缓存
三级缓存是Spring框架里,一个经典的技术点,它很好地解决了循环依赖的问题,也是很多面试中会被问到的问题,本文从源码入手,详细剖析Spring三级缓存的来龙去脉。
1979 24
Spring是如何解决循环依赖的?从底层源码入手,详细解读Spring框架的三级缓存
|
缓存 NoSQL Java
springboot的缓存和redis缓存,入门级别教程
本文介绍了Spring Boot中的缓存机制,包括使用默认的JVM缓存和集成Redis缓存,以及如何配置和使用缓存来提高应用程序性能。
689 1
springboot的缓存和redis缓存,入门级别教程