Java - SpringBoot整合Shiro之缓存功能

news2025/7/6 7:04:34

Java - SpringBoot整合Shiro之缓存功能

  • 前言
  • 一. SpringBoot 整合Redis
    • 1.1 配置 RedisTemplate
    • 1.2 Shiro整合Redis缓存配置
    • 1.3 测试

前言

在 上一篇文章 主要讲了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的请求,都不需要经过认证了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/7038.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

百趣代谢组学文献分享:真假肥胖?代谢组说了算

肥胖是当今社会面临的很普遍的健康问题之一&#xff0c;超重会显著增加患糖尿病和心血管疾病的风险。而在日常生活中&#xff0c;我们发现有些肥胖者健康状况良好&#xff0c;而有些相对较瘦的人&#xff0c;却存在患糖尿病和心血管疾病的风险。 百趣代谢组学文献分享&#xf…

基于STM32结合CubeMX学习Free-RT-OS的源码之信号量与互斥量

目录 CUBEMX上的配置以及使用 信号量 互斥量 CUBEMX上的配置以及使用 信号量与互斥量都是从队列中衍生出来的&#xff0c;他们是一种特殊的队列。不同的地方在于:他们不含有队列的数据部分&#xff0c;只有队列结构体。 定义属性&#xff08;这里只有一个名字&#xff09;和…

Handler 消息队列中的同步屏障——Message

Message 分为3种&#xff1a;普通消息&#xff08;同步消息&#xff09;、屏障消息&#xff08;同步屏障&#xff09;和异步消息。我们通常使用的都是普通消息&#xff0c;而屏障消息就是在消息队列中插入一个屏障&#xff0c;在屏障之后的所有普通消息都会被挡着&#xff0c;不…

隐式类型转换(整形提升)

隐式类型转换1.定义2.整形提升例子3.char的取值范围和一些技巧1.定义 1.c的整形算术运算总是至少以缺省整形类型的精度来进行的。 2.为了获取这个精度&#xff0c;像字符型&#xff0c;短整形在使用之前会转换为整形&#xff0c;这种转换被称为整形提升 3.整形提升时补最高位的…

Baklib|信息管理和知识管理是如何影响你的业务的?

有效的信息和知识管理可以让您消除库和共享知识。本文讨论了信息管理和知识管理的来龙去脉。信息管理和知识管理通常可以互换使用&#xff0c;但也有关键的区别。了解这些差异以及它们如何影响您的业务&#xff0c;可以使您优化管理策略、简化工作流程并提高生产率。 本文涵盖…

灰色GM(1,1)模型及其在电力负荷预测中的应用附Matlab代码

✅作者简介&#xff1a;热爱科研的Matlab仿真开发者&#xff0c;修心和技术同步精进&#xff0c;matlab项目合作可私信。 &#x1f34e;个人主页&#xff1a;Matlab科研工作室 &#x1f34a;个人信条&#xff1a;格物致知。 更多Matlab仿真内容点击&#x1f447; 智能优化算法 …

【每日一题】LFU 缓存

一个缓存结构需要实现如下功能&#xff1a; void set(int key,int value)&#xff1a;加入或者修改 key 对应的 value int get(int key)&#xff1a;查询 key 对应的 value 值 但是缓存最多放 K 条记录&#xff0c;如果新的 K 1 条记录需要加入&#xff0c;就需要根据策略删掉…

【面试题】如何替换项目中的if-else和switch

给大家推荐一个实用面试题库 1、前端面试题库 &#xff08;面试必备&#xff09; 推荐&#xff1a;★★★★★ 地址&#xff1a;前端面试题库 在项目中&#xff0c;往往会看到很多的if-else或者switch&#xff0c;项目会变得很臃肿&#xff0c;而且不易阅读&…

速溶颗粒:实验中的好伙伴

缓冲溶液 (buffer solution) 通常是由弱酸及其盐、弱碱及其盐组成的混合溶液&#xff0c;能在一定程度上抵消、减轻外加强酸或强碱对溶液酸碱度的影响&#xff0c;从而保持溶液的 pH 值相对稳定。 传统的缓冲液配制过程可简单概括为计算——称量——溶解——定容。而生物学上常…

windows10提权

参照tryhackme的win10提权靶场 靶场&#xff0c;地址 里面共描述了服务路径&#xff0c;文件权限&#xff0c;计划任务&#xff0c;令牌窃取&#xff0c;图形化软件&#xff0c;应用组件安装等&#xff0c;这里只有令牌窃取需要管理员Administrator权限&#xff0c;值得注意的是…

向毕业妥协系列之机器学习笔记:无监督学习-聚类

目录 序言 一.什么是聚类 二.K-means算法 三.优化目标 四.初始化K-means 五.选择聚类数量&#xff08;k?&#xff09; 序言 第三课这块要学习的几块知识如下&#xff1a; 在学完监督学习之后&#xff0c;接下来我们要学习的东西分别是聚类&#xff0c;异常检测&#xf…

Spring 源码阅读 74:事务管理的原理 - BeanFactoryTransactionAttributeSourceAdvisor 分析

本文通过对 BeanFactoryTransactionAttributeSourceAdvisor 类的分析&#xff0c;了解了 Spring 是如何通过 AOP 来完成事务的管理的&#xff0c;本文的内容需要你对 Spring 的 AOP 的实现原理有一定的了解。 基于 Spring Framework v5.2.6.RELEASE 概述 Spring 的事务管理基于…

基于 Flask-Admin 与 AdminLTE 构建通用后台管理系统

Flask-Admin 是什么&#xff1f; Flask-Admin 官网文档中给出了其功能定位&#xff1a; Why Flask-Admin? In a world of micro-services and APIs, Flask-Admin solves the boring problem of building an admin interface on top of an existing data model. With little e…

SAP 公司代码全局参数设置及其意义

在SAP中配置公司时&#xff0c;会配置公司的全局参数&#xff0c;但这些参数具体的意思是什么估计很多同学都搞不懂&#xff0c;我也找了下资料&#xff0c;贴出来供大家参考。 设置参数路径&#xff1a;IMG→财务会计→财务会计全局设置→公司代码的全球参数→输入全局参数 账…

教你几个手机识别图片中的文字小技巧

平时我们在工作&#xff0c;有时候会拿到需要录入的纸质文件&#xff0c;如果我们使用双手逐一对照录入的话&#xff0c;就太浪费时间了。其实还有一个更简单的方法&#xff0c;就是将需要录入的文件拍摄下来&#xff0c;借助工具将图片内容转写为文字出来&#xff0c;再将其复…

Python Flask框架-开发简单博客-认证蓝图

作者&#xff1a;Eason_LYC 悲观者预言失败&#xff0c;十言九中。 乐观者创造奇迹&#xff0c;一次即可。 一个人的价值&#xff0c;在于他所拥有的。可以不学无术&#xff0c;但不能一无所有&#xff01; 技术领域&#xff1a;WEB安全、网络攻防 关注WEB安全、网络攻防。我的…

最新定制的安卓项目及设计报告——仿番茄小说APP

已录演示视频&#xff0c;想看演示视频的可以私我 《移动应用开发实践》实践报告 APP名称&#xff1a; 番茄免费小说 要求&#xff1a; 格式&#xff1a;宋体&#xff0c;小四号字&#xff1b;首行缩进&#xff1b;行距&#xff1a;1.5倍。 每人独立完成Android App的设计…

三步学会如何构建平衡二叉树(简单好理解)

何为平衡二叉树? 首先回顾一下&#xff0c;什么是平衡二叉树&#xff08;亦被称为AVL树&#xff0c;Adelson-Velskii and Landis&#xff09;。平衡二叉树主要具有以下三个特点&#xff1a; 1. 平衡二叉树首先要符合搜索二叉树的特点&#xff1a;即左子树的值比根节点小&…

排序算法之归并排序

目录 归并排序递归实现 思想 图解 代码 归并排序的非递归版本 基本思想&#xff1a; 代码 归并排序递归实现 思想 最主要的相当于二叉树遍历中的后序遍历。 ①将数组分割成多个小区间&#xff08;当只有一个元素或者并不存在的时候就不用再分割了&#xff09; ②对每一…

某工控图片上传服务 CPU 爆高分析

一&#xff1a;背景 1.讲故事 今天给大家带来一个入门级的 CPU 爆高案例&#xff0c;前段时间有位朋友找到我&#xff0c;说他的程序间歇性的 CPU 爆高&#xff0c;不知道是啥情况&#xff0c;让我帮忙看下&#xff0c;既然找到我&#xff0c;那就用 WinDbg 看一下。 二&…