Java - SpringBoot整合Shiro之缓存功能

前言

上一篇文章 主要讲了Shiro权限授权和认证跳过。本篇文章就主要讲解如何整合ShiroRedis。这样就避免携带同一个Token的时候,每次取查询数据库了。可以利用一个缓存,减轻DB的压力。

一. SpringBoot 整合Redis

添加pom依赖:

<!--redis整合springboot-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

1.1 配置 RedisTemplate

我们可以在application.yml文件中添加Redis相关的配置:

spring:
  redis:
    database: 0 # Redis数据库索引(默认为0)
    host: xxx # Redis的服务地址
    port: xxx # Redis的服务端口
    password: xxx # Redis密码
    jedis:
      pool:
        max-active: 8 # 连接池最大连接数(使用负值表示没有限制)
        max-wait: -1 # 连接池最大阻塞等待时间(使用负值表示没有限制)
        max-idle: 8 # 连接池中的最大空闲连接
        min-idle: 0 # 连接池中的最小空闲链接
    timeout: 30000 # 连接池的超时时间(毫秒)

添加Redis的配置类RedisConfig

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

/**
 * @Date 2022/11/15 13:56
 * @Created by jj.lin
 */
@Configuration
public class RedisConfig {
    /**
     * 实例化 RedisTemplate 对象
     *
     * @return
     */
    @Bean
    public RedisTemplate<String, Object> functionDomainRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        initDomainRedisTemplate(redisTemplate, redisConnectionFactory);
        return redisTemplate;
    }
    /**
     * 设置数据存入 redis 的序列化方式,并开启事务
     *
     * @param redisTemplate
     * @param factory
     */
    private void initDomainRedisTemplate(RedisTemplate<String, Object> redisTemplate, RedisConnectionFactory factory) {
        // 配置序列化器。如果不配置Serializer,那么存储的时候缺省使用String,如果用User类型存储,那么会提示错误User can't cast to String!
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());

        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());

        // 开启事务
        redisTemplate.setEnableTransactionSupport(true);
        redisTemplate.setConnectionFactory(factory);
    }
}

1.2 Shiro整合Redis缓存配置

1.我们自定义一个缓存管理器RedisCacheManager,需要继承CacheManager

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

/**
 * @Date 2022/11/15 13:52
 * @Created by jj.lin
 */
public class RedisCacheManager implements CacheManager {
    @Override
    public <K, V> Cache<K, V> getCache(String s) throws CacheException {
        return new RedisCache<K,V>(s);
    }
}

2.上面的RedisCache是我们自定义的实现,我们需要重写相关的put/get函数。这样Shiro在认证的时候,就会通过我们自定义的put/get函数去存储/获得缓存了。

import com.pro.config.SpringBeanUtil;
import com.pro.constant.JwtConstant;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheException;
import org.springframework.data.redis.core.RedisTemplate;

import java.util.Collection;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * @Date 2022/11/15 13:52
 * @Created by jj.lin
 */
@Slf4j
public class RedisCache<K, V> implements Cache<K, V> {
    private String cacheName;

    public RedisCache(String cacheName) {
        this.cacheName = cacheName;
    }
    
    private RedisTemplate getRedisTemplate() {
        RedisTemplate redisTemplate = SpringBeanUtil.getBean(RedisTemplate.class);
        return redisTemplate;
    }

    @Override
    public V get(K k) throws CacheException {
        RedisTemplate redisTemplate = getRedisTemplate();
        V v = (V) redisTemplate.opsForHash().get(cacheName, k.toString());
        Long expire = redisTemplate.getExpire(cacheName);
        log.info("从 {} 中获取Key:{}, Value:{},剩余缓存时长:{}", cacheName, k, v, expire);
        return v;
    }

    @Override
    public V put(K k, V v) throws CacheException {
        log.info("从 {} 中插入Key:{}, Value:{}", cacheName, k, v);
        RedisTemplate redisTemplate = getRedisTemplate();
        redisTemplate.opsForHash().put(cacheName, k.toString(), v);
        // 可以设置一个缓存的超时时间
        redisTemplate.expire(cacheName, JwtConstant.TIMEOUT, TimeUnit.MINUTES);
        return null;
    }

    @Override
    public V remove(K k) throws CacheException {
        return (V) getRedisTemplate().opsForHash().delete(cacheName, k.toString());
    }

    @Override
    public void clear() throws CacheException {
        getRedisTemplate().delete(cacheName);
    }

    @Override
    public int size() {
        return getRedisTemplate().opsForHash().size(cacheName).intValue();
    }

    @Override
    public Set<K> keys() {
        return getRedisTemplate().opsForHash().keys(cacheName);
    }

    @Override
    public Collection<V> values() {
        return getRedisTemplate().opsForHash().values(cacheName);
    }
}

3.常量JwtConstant

public class JwtConstant {
    public static final String USER_ID = "userId";
    public static final Integer TIMEOUT = 10;
}

4.然后Shiro开启缓存功能的配置ShiroConfig

原代码:
@Bean
public Realm realm() {
    JwtRealm jwtRealm = new JwtRealm();
    return jwtRealm;
}
新代码:
@Bean
public Realm realm() {
    JwtRealm jwtRealm = new JwtRealm();

    // 开启缓存,设置缓存管理器
    jwtRealm.setCachingEnabled(true);
    jwtRealm.setAuthenticationCachingEnabled(true);
    jwtRealm.setAuthorizationCachingEnabled(true);
    jwtRealm.setCacheManager(new RedisCacheManager());
    return jwtRealm;
}

1.3 测试

1.我们先登录一下:拿到一个最新的Token
在这里插入图片描述

2.第一次访问getUser接口(需要经过JWT认证):
在这里插入图片描述
为什么这里有两次插入?因为我getUser接口还有身份校验的过程:

@RequiresRoles("user")
@PostMapping("/getUser")
public String getUser() {
    Long userId = JwtUtil.getUserId();
    Subject currentUser = SecurityUtils.getSubject();
    if (userId != null) {
        return "成功拿到用户信息: " + currentUser.getPrincipal();
    }
    return "用户信息为空";
}

相当于:

  • doGetAuthenticationInfo:第一次认证的时候,插入了一条缓存。
  • doGetAuthorizationInfo:第二次身份校验的时候,又更新了一次缓存。

3.第二次访问:可见没有插入的日志了。同时Degbug打个断点也能发现,上面两个函数,只有第一次执行的时候会访问。后续就不会再进入了。因为缓存的原因。
在这里插入图片描述

我们存入Redis中的hashKey就是我们的Token。所以在有效时间内,带着相同Token的请求,都不需要经过认证了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Zong_0915

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值