0
点赞
收藏
分享

微信扫一扫

shiro缓存配置流程


以前搞shiro的时候没有刻意去研究过这些配置文件,导致用shiro的时候也是迷迷糊糊,惭愧啊,要想成为人上人,读源码,懂配置是真滴重要!

安全管理器配置(SecurityManager )

配置项

意思


setCacheManager

配置缓存管理器用来缓存realm和session信息


setRealm

登录时假如用的UsernamePasswordToken,会来调用我们这里设置过的realm中找符合条件的realm,执行对应的逻辑


setSessionManager

配置会话管理器

/**
     * @param
     * @method 这里设置了shiroCacheManager,表示要缓存loginRealm,以及会话session。
     * 这里如果不设置shiroCacheManager,由于shiroSessionDao(shiroCacheManager)已经注入过shiroCacheManager
     * session信息也是能被缓存的
     */
    @Bean
    public SecurityManager securityManager() {
        DefaultSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setCacheManager(shiroCacheManager);
        securityManager.setRealm(loginRealm());
        securityManager.setSessionManager(sessionManager());
        return securityManager;
    }

    @Bean
    public loginRealm loginRealm() {
        return new loginRealm();
    }
    
    /**
     * @param
     * @method DefaultWebSessionManager的配置,设置了sessionDAO
     */
    @Bean
    public SessionManager sessionManager() {
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        sessionManager.setSessionDAO(sessionDAO());
        return sessionManager;
    }

    /**
     * @param
     * @method 这里配置shiro的会话底层的缓存管理器,底层用的是redis缓存会话
     */
    @Bean
    public SessionDAO sessionDAO() {
        shiroSessionDao shiroSessionDao = new shiroSessionDao(shiroCacheManager);
        return shiroSessionDao;
    }

注意点:

/**
     * @param
     * @method 这里设置了shiroCacheManager,表示要缓存loginRealm,以及会话session。
     * 这里如果不设置shiroCacheManager,由于shiroSessionDao(shiroCacheManager)已经注入过shiroCacheManager
     * session信息也是能被缓存的
     */

    @Bean
    public SecurityManager securityManager() {
        DefaultSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setCacheManager(shiroCacheManager);
        securityManager.setRealm(loginRealm());
        securityManager.setSessionManager(sessionManager());
        return securityManager;
    }

为什么要配置缓存管理器(setCacheManager)?

不配置缓存管理器: 每次去访问那些需要权限的页面的时候都要执行授权的操作,授权里面又要查权限表,这个页面才会知道到底你是否有权限访问,带来的开销是巨大的。
配置缓存管理器: 只需从缓存里面拿取权限信息就行。

开始配置

首先我们要明白我们要配置的是shiro中的CacheManager

shiro缓存配置流程_redis

shiro缓存配置流程_分布式_02

思路:知道这个那就简单了,直接编写其实现类就是了,只不过获取cache的途径是从shiro.cache中获取而已,而shiro.cache中获取cahce的途径是从redis中获取的,怎么从redis获取的?我们直接往shiro.cache中注入一个RedisCacheManager 不就得了。进而实现shiro.cache中的crud,完事。

public class shiroCacheManager implements CacheManager {

    private static RedisCacheManager redisCacheManager;

    public shiroCacheManager(RedisCacheManager redisCacheManager) {
        this.redisCacheManager = redisCacheManager;
    }

    @Override
    public <K, V> Cache<K, V> getCache(String cacheName) throws CacheException {
        return new shiroCache(redisCacheManager, cacheName);
    }
}

自定义Cache<k, v>(shiro提供的cache接口)

根据传进来的 RedisCacheManager 获取对应组的 spring.cache(RedisCacheManager是由Spring提供的,获取到的cache 是spring.cache),然后用这个spring.cache进行crud操作。为了让代码复用性高一点我们用范型来做处理

/**
 * @author 张子行
 * @class 自定义shiro缓存
 */
public class shiroCache<k, v> implements Cache<k, v> {
    private  org.springframework.cache.Cache springCache;
    private  RedisCacheManager redisCacheManager;
    private  String cacheName;
    public shiroCache(RedisCacheManager redisCacheManager, String cacheName) {
        this.springCache = redisCacheManager.getCache(cacheName);
        this.redisCacheManager = redisCacheManager;
        this.cacheName = cacheName;
    }

    /**
     * @param
     * @method 缓存的获取
     * 在访问需要权限的页面时会,从缓存中拉去权限信息,看你是否有权访问
     */
    @Override
    public v get(k k) throws CacheException {
        System.out.println("get():"+cacheName);
        org.springframework.cache.Cache.ValueWrapper valueWrapper = springCache.get(k);
        if (valueWrapper != null) {
            return (v) valueWrapper.get();
        }
        return null;
    }

    /**
     * @param
     * @method 缓存的放入,在会话开始的时候会把session信息放入缓存,
     * 在subject.login()的时候会把授权信息放入缓存
     */
    @Override
    public v put(k k, v v) throws CacheException {
        System.out.println("put():"+cacheName);
        springCache.put(k, v);
        return v;
    }

    /**
     * @param
     * @method 缓存的清除单个(在Subject subject = SecurityUtils.getSubject();
     *         subject.logout();的时候会清除相应的授权认证缓存,我测试的时候session缓存也会被清除)
     */
    @Override
    public v remove(k k) throws CacheException {
        System.out.println("remove():"+cacheName);
        v v = this.get(k);
        springCache.evict(k);
        return v;
    }

    /**
     * @param
     * @method 缓存的全部删除
     */
    @Override
    public void clear() throws CacheException {
        System.out.println("clear(): "+cacheName);
        springCache.clear();
    }

    /**
     * @param
     * @method 缓存的个数
     */
    @Override
    public int size() {
        System.out.println("size(): "+cacheName);
        int size = this.keys().size();
        return size;
    }

    /**
     * @param
     * @method 缓存的所有key
     */
    @Override
    public Set<k> keys() {
        System.out.println("keys(): "+cacheName);
        Collection<String> cacheNames = redisCacheManager.getCacheNames();
        return (Set<k>) cacheNames;
    }

    /**
     * @param
     * @method 缓存的所有values
     */
    @Override
    public Collection<v> values() {
        System.out.println("values(): "+cacheName);
        ArrayList<v> data = new ArrayList<>();
        Set<k> keys = this.keys();
        for (k k : keys) {
            data.add(this.get(k));
        }
        return data;
    }
}

自定义CachingSessionDAO

/**
 * @author 张子行
 * @class 自定义CachingSessionDAO
 */
public class shiroSessionDao extends CachingSessionDAO {

    private static Serializable sessionId = null;

    /**
     * @param
     * @method 提供缓存session的扩展方法,到时候用的时候,
     * 想用什么缓存session信息,直接注入对应的CacheManager就行
     */
    public shiroSessionDao(CacheManager cacheManager) {
        super.setCacheManager(cacheManager);
    }

    @Override
    protected void doUpdate(Session session) {
        System.out.println("更新session");
    }

    @Override
    protected void doDelete(Session session) {
        System.out.println("删除session");
    }

    @Override
    protected Serializable doCreate(Session session) {
        sessionId = "666";
        assignSessionId(session, sessionId);
        return sessionId;
    }

    @Override
    protected Session doReadSession(Serializable sessionId) {
        System.out.println("读取session");
        return null;
    }

    /**
     * @param
     * @method 把session对象转化为byte保存到redis中
     */
    public byte[] sessionToByte(Session session) {
        ByteArrayOutputStream bo = new ByteArrayOutputStream();
        byte[] bytes = null;
        try {
            ObjectOutputStream oo = new ObjectOutputStream(bo);
            oo.writeObject(session);
            bytes = bo.toByteArray();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return bytes;
    }
}

注入缓存管理器

/**
 * @author 张子行
 * @class 缓存管理器
 */
@Configuration
public class cacheConfig {
    @Autowired
    RedisConnectionFactory redisConnectionFactory;

    @Bean(name = "shiroCacheManager")
    public shiroCacheManager shiroCacheManager() {
        RedisCacheConfiguration conf = RedisCacheConfiguration.defaultCacheConfig();
        //shiro配置的缓存管理器是这个,这里是设置登录的过期时间,及其session的过期时间,按秒为单位
        conf = conf.entryTtl(Duration.ofSeconds(300000));
        RedisCacheManager cacheManager = RedisCacheManager
                .builder(redisConnectionFactory)
                .cacheDefaults(conf)
                .build();
        shiroCacheManager shiroCacheManager = new shiroCacheManager(cacheManager);
        return shiroCacheManager;
    }

    @Bean(name = "redisCacheManager")
    public RedisCacheManager redisCacheManager() {
        RedisCacheConfiguration cacheConfig = RedisCacheConfiguration.defaultCacheConfig();
        RedisCacheManager redisCacheManager = RedisCacheManager.builder(redisConnectionFactory)
                .cacheDefaults(cacheConfig)
                .build();
        return redisCacheManager;
    }
}

测试

当我们登录的时候(subject.login),发现redis里面确实把 认证信息,session数据写进去了

shiro缓存配置流程_java_03


不断刷新这个页面,发现压根不会进入loginrealm中的授权这,如果没有配置缓存管理器,那么每次刷新都会走授权

shiro缓存配置流程_shiro_04

接着我们登出的时候也就是执行下面的代码时候,数据成功移除realm和session数据

Subject subject = SecurityUtils.getSubject();
subject.logout();

shiro缓存配置流程_缓存_05

总结

在集成到shiro中的时候,自己编写 CacheManager 实现对Cache的增删改查会出现很多bug(不建议造轮子),本文的代码也就是为了让大家理解 shiro 的几大核心组件以及缓存生效的配置规则而已。


举报

相关推荐

0 条评论