Shiro学习(五):Shiro对权限的缓存

发布于:2025-04-05 ⋅ 阅读:(19) ⋅ 点赞:(0)

一、问题描述

       由前边的学习中了解,用户的角色权限一般存储在数据库中,每次进行权限校验时都要从

       数据库查询用户的角色权限信息;对数据库来说这样频繁的查询压力太大了,也影响程序的

       性能。

       Shiro 中执行权限角色校验时,默认也是优先从缓存中查询用户权限信息,若缓存中查询不到

       用户信息,然后才去数据库中查询用户信息。

       前边笔记(三)RolesAuthorizationFilter 分析中,AuthorizingRealm.getAuthorizationInfo 方

       法中先执行 getAvailableAuthorizationCache() 方法从缓存中查询对应信息,如下图所示:

               

       

二、基于JVM内存的权限缓存

       Shiro 中虽然默认提供了基于JVM内存的缓存机制,但是默认是没开启,权限角色信息并不会

       缓存;因为默认情况下 DefaultWebSecurityManager 中 CacheManager 为null,

       如下图所示:

             

      shiro 默认提供的jvm缓存是 MemoryConstrainedCacheManager,它是把权限角色信息缓存到

     Map 中,如下所示:

              

              

       1、如何开启Shiro 缓存

            开启 Shiro 缓存功能也很简单,只要把 MemoryConstrainedCacheManager 注入到 

            DefaultWebSecurityManager 中就行了,示例代码如下:

    @Bean
    public DefaultWebSecurityManager securityManager(CustomRealm realm, SessionManager sessionManager ){
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(realm);

        //将使用的SessionManager注入到SecurityManager
        securityManager.setSessionManager(sessionManager);
        //使用Shiro 提供的基于JVM 内存的 CacheManager
        MemoryConstrainedCacheManager cacheManager = new MemoryConstrainedCacheManager();
        securityManager.setCacheManager(cacheManager);

        return securityManager;
    }

三、基于Redis的权限缓存

       将权限信息存储到内存中在实际工作用有很多限制,如:占用内存资源、只适用于单机环境

       、缓存周期短等等。

       在实际工作中一般将权限信息存储到中间件Redis 中,实现步骤如下:

       1)模仿 MapCache 定义 RedisCache 用于将数据保存到Redis 中,有一点要注意,

             RedisCache需要实现 org.apache.shiro.cache 包下的接口 Cache,

             示例代码如下:

@Component
public class RedisCache<K,V> implements Cache<K,V> {

    @Autowired
    private RedisTemplate redisTemplate;

    private final String CACHE_PREFIX = "cache:";

    /**
     * 获取授权缓存信息
     * @param k
     * @return
     * @throws CacheException
     */
    @Override
    public V get(K k) throws CacheException {
        System.out.println("从redis 查询权限信息~~~~~~~~~~~");
        V v = (V) redisTemplate.opsForValue().get(CACHE_PREFIX + k);
        if(v != null){
            redisTemplate.expire(CACHE_PREFIX + k,15, TimeUnit.MINUTES);
        }
        return v;
    }

    /**
     * 存放缓存信息
     * @param k
     * @param v
     * @return
     * @throws CacheException
     */
    @Override
    public V put(K k, V v) throws CacheException {
        redisTemplate.opsForValue().set(CACHE_PREFIX + k,v,15,TimeUnit.MINUTES);
        return v;
    }

    /**
     * 清空当前缓存
     * @param k
     * @return
     * @throws CacheException
     */
    @Override
    public V remove(K k) throws CacheException {
        V v = (V) redisTemplate.opsForValue().get(CACHE_PREFIX + k);
        if(v != null){
            redisTemplate.delete(CACHE_PREFIX + k);
        }
        return v;
    }

    /**
     * 清空全部的授权缓存
     * @throws CacheException
     */
    @Override
    public void clear() throws CacheException {
        Set keys = redisTemplate.keys(CACHE_PREFIX + "*");
        redisTemplate.delete(keys);
    }

    /**
     * 查看有多个权限缓存信息
     * @return
     */
    @Override
    public int size() {
        Set keys = redisTemplate.keys(CACHE_PREFIX + "*");
        return keys.size();
    }

    /**
     * 获取全部缓存信息的key
     * @return
     */
    @Override
    public Set<K> keys() {
        Set keys = redisTemplate.keys(CACHE_PREFIX + "*");
        return keys;
    }

    /**
     * 获取全部缓存信息的value
     * @return
     */
    @Override
    public Collection<V> values() {
        Set values = new HashSet();
        Set keys = redisTemplate.keys(CACHE_PREFIX + "*");
        for (Object key : keys) {
            Object value = redisTemplate.opsForValue().get(key);
            values.add(value);
        }
        return values;
    }
}

       2)模仿 MemoryConstrainedCacheManager 定义 RedisCacheManager;

             RedisCacheManager 实现 org.apache.shiro.cache 包下的接口,并重写 getCache 方法

             示例代码如下:

@Component
public class RedisCacheManager implements CacheManager {

    @Autowired
    private RedisCache redisCache;

    /**
     * 返回 Cache 的实现类
     * @param s
     * @return
     * @param <K>
     * @param <V>
     * @throws CacheException
     */
    @Override
    public <K, V> Cache<K, V> getCache(String s) throws CacheException {
        return redisCache;
    }
}

      3)将自定义的 RedisCacheManager 注入到  DefaultWebSecurityManager 中;

           示例代码如下:

 @Bean
    public DefaultWebSecurityManager securityManager(CustomRealm realm, SessionManager sessionManager, RedisCacheManager cacheManager){
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(realm);

        //将使用的SessionManager注入到SecurityManager
        securityManager.setSessionManager(sessionManager);
        //使用自定义基于Redis 缓存的 RedisCacheManager
        securityManager.setCacheManager(cacheManager);

        return securityManager;
    }


网站公告

今日签到

点亮在社区的每一天
去签到