首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Shiro 集成 Spring 之授权缓存

Shiro 集成 Spring 之授权缓存

作者头像
一份执着✘
发布2018-10-08 10:01:02
6070
发布2018-10-08 10:01:02
举报
文章被收录于专栏:赵俊的Java专栏赵俊的Java专栏

前言

手撸 Java Web RBAC 权限管理 中,我们自己实现了一个简易的 RBAC 权限管理框架,且我们也提到了一些缺陷,其中一点就是 : 每次请求需要授权的页面都会去数据库查询此用户对应的权限数据和角色数据,太耗费资源,应该进行缓存。

本章我们就来讲讲如何将 Shiro 中的授权数据缓存到 Redis 中。

API

Shiro 为授权数据的缓存提供了两个借口,一个是 CacheManager,一个是 Cache

根据这两个接口,我们完全可以将授权数据缓存到任何地方,包括 redisehcache 、内存等。

Redis

既然我们要缓存到 Redis 中,我们需要搭建 Redis 环境,并导入 Redis 工具类:

<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>2.9.0</version>
</dependency>

新建配置文件 spring-redis.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="jedisPool" class="redis.clients.jedis.JedisPool">
        <!-- Jedis 配置信息 -->
        <constructor-arg name="poolConfig" ref="jedisPoolConfig"/>
        <!-- Redis URL -->
        <constructor-arg name="host" value="127.0.0.1"/>
        <!-- Redis 端口-->
        <constructor-arg name="port" value="6379"/>
        <!-- Redis 密码 -->
        <!--<constructor-arg value=""/>-->
    </bean>

    <bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
        <!-- 最大连接数 -->
        <property name="maxTotal" value="500"/>
        <!-- 最大闲置 -->
        <property name="maxIdle" value="100"/>
        <!-- 最小闲置 -->
        <property name="minIdle" value="10"/>
        <!-- 最大等待 -->
        <property name="maxWaitMillis" value="5000"/>
        <!-- 可以获取 -->
        <property name="testOnBorrow" value="true"/>
    </bean>
</beans>

Cache

我们来创建一个 RedisCache 继承自 org.apache.shiro.cache.Cache,来实现它的方法:

package im.zhaojun.cache;

import im.zhaojun.util.JedisUtil;
import org.apache.log4j.Logger;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheException;
import org.springframework.stereotype.Component;
import org.springframework.util.SerializationUtils;

import javax.annotation.Resource;
import java.util.Collection;
import java.util.Set;

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

    private static final Logger logger = Logger.getLogger(RedisCache.class);

    @Resource
    private JedisUtil jedisUtil;

    private final String CACHE_PREFIX = "shiro-cache:";

    private byte[] getKeyBytes(K k) {
        return (CACHE_PREFIX + k).getBytes();
    }

    @Override
    public V get(K k) throws CacheException {
        logger.info("从 Redis 中读取授权信息...");
        byte[] key = getKeyBytes(k);
        byte[] value = jedisUtil.get(key);
        if (value != null) {
            return (V) SerializationUtils.deserialize(value);
        }
        return null;
    }

    @Override
    public V put(K k, V v) throws CacheException {
        byte[] key = getKeyBytes(k);
        byte[] value = SerializationUtils.serialize(v);
        jedisUtil.set(key, value);
        jedisUtil.expire(key, 600);
        return v;
    }

    @Override
    public V remove(K k) throws CacheException {
        byte[] key = getKeyBytes(k);
        byte[] value = jedisUtil.get(key);
        jedisUtil.del(key);

        if (value != null) {
            SerializationUtils.deserialize(value);
        }
        return null;
    }

    @Override
    public void clear() throws CacheException {
        jedisUtil.delKeysByPrefix(CACHE_PREFIX);
    }

    @Override
    public int size() {
        return jedisUtil.getKeysByPrefix(CACHE_PREFIX).size();
    }

    @Override
    public Set<K> keys() {
        return (Set<K>) jedisUtil.getKeysByPrefix(CACHE_PREFIX);
    }

    @Override
    public Collection<V> values() {
        return jedisUtil.getValuesByPrefix(CACHE_PREFIX);
    }
}

其中没什么难点,只是对 redis 的基本增删改查操作,由于是存储到 redis 中,所以我们为缓存数据的 key 添加了前缀,以便再次获取。

CacheManager

我们创建一个 RedisCacheManager 类来继承自 org.apache.shiro.cache.AbstractCacheManager,当然你也可以直接继承自 org.apache.shiro.cacheCacheManager

package im.zhaojun.cache;

import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheException;
import org.apache.shiro.cache.AbstractCacheManager;

import javax.annotation.Resource;

@Component
public class RedisCacheManager extends AbstractCacheManager  {

    @Resource
    private RedisCache redisCache;

    @Override
    protected Cache createCache(String s) throws CacheException {
        return redisCache;
    }
}

这里在 createCache() 方法中返回我们的自定义 RedisCache 对象即可。

Spring

然后我们将 RedisCacheManager 配置到 securityManager 中:

<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
    <property name="realm" ref="myRealm"/>
    <property name="cacheManager" ref="redisCacheManager"/>
</bean>

以及将 spring-redis.xml 配置到 web.xml 中:

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>
        classpath:spring.xml,
        classpath:spring-shiro.xml,
        classpath:spring-redis.xml
    </param-value>
</context-param>

Test

然后我们分别在 RealmdoGetAuthorizationInfo 方法和 RedisCacheget 方法中分别打印一条日志,看何时会访问数据库,何时会访问 Redis 缓存的数据。

  • 首先是未认证的情况下,访问需要权限的的页面,不会输出任何信息,因为需要认证后,才会根据认证信息去获取授权现象,没有认证时,会直接拦截。
  • 认证之后,访问需要授权的页面,会输入如下信息: im.zhaojun.cache.RedisCache 15:09:14,015 INFO RedisCache:30 - 从 Redis 中读取授权信息... im.zhaojun.realm.MyRealm 15:09:14,016 INFO MyRealm:23 - 从数据库中读取授权信息... 由此可见,Shiro 会先去 Redis 中取数据,如果 Redis 中没有,再去 Realm(数据库) 中取。 然后再次访问这个页面,输入: im.zhaojun.cache.RedisCache 15:11:13,351 INFO RedisCache:30 - 从 Redis 中读取授权信息... 因为缓存中已经有了,就不再去数据库中查询了。

小结

其实频繁从 Redis 中读取也是比较浪费资源的, Redis 的连接同样宝贵,最好的办法还是直接存储在内存中,但也是各有利弊,需要根据实际项目来决定使用哪种方案。

放到 Redis 的好处是:可以用来做跨项目/机器的数据缓存,可以集群,持久化等。 放到内存的好处是:速度快,使用方便快捷。 但使用这种缓存还有一个比较重要的事情,就是当数据库中的授权数据发生修改时,也要记得刷新缓存中的数据,不然会出现数据错乱,实现方式可以通过直接覆盖缓存,消息队列通知等方式,需要根据不同项目来选区不同方式,由于篇幅原因这里不再展开讲了。

本章代码地址 : https://github.com/zhaojun1998/Premission-Study/tree/master/Permission-Shiro-08/

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018-08-31,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • API
  • Redis
  • Cache
  • CacheManager
  • Spring
  • Test
  • 小结
相关产品与服务
云数据库 Redis
腾讯云数据库 Redis(TencentDB for Redis)是腾讯云打造的兼容 Redis 协议的缓存和存储服务。丰富的数据结构能帮助您完成不同类型的业务场景开发。支持主从热备,提供自动容灾切换、数据备份、故障迁移、实例监控、在线扩容、数据回档等全套的数据库服务。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档