SpringBoot进阶之Shiro实现缓存和会话管理功能

本文涉及的产品
云数据库 Tair(兼容Redis),内存型 2GB
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
简介: SpringBoot进阶之Shiro实现缓存和会话管理功能

前言

大家好,一直以来我都本着用最通俗的话理解核心的知识点, 我认为所有的难点都离不开 基础知识 的铺垫。目前正在出一个SpringBoot长期系列教程,从入门到进阶, 篇幅会较多~


适合人群

  • 学完Java基础
  • 想通过Java快速构建web应用程序
  • 想学习或了解SpringBoot
  • SpringBoot进阶学习

大佬可以绕过 ~


背景

如果你是一路看过来的,很高兴你能够耐心看完。之前带大家学了Springboot基础部分,对基本的使用有了初步的认识, 接下来的几期内容将会带大家进阶使用,会先讲解基础中间件的使用和一些场景的应用,或许这些技术你听说过,没看过也没关系,我会带大家一步一步的入门,耐心看完你一定会有收获~


情景回顾

上期带大家学习了Shiro中如何进行权限认证,本期将带大家学习Shiro中如何进行缓存和会话管理,最后我们将做一个在线用户管理以及强制下线用户的功能,同样的,我们集成到Springboot中。


缓存集成

首先我们要明白使用缓存的原因,为啥要用它❓还记得之前带大家实现的用户认证权限认证吗,那里我使用了MockUser,真实场景中是要去数据查询的,这样一来就会产生耗时,请求多的时候数据库肯定忙不过来了,所以我们需要使用缓存来提高程序响应速度


缓存使用Redis,下面就带大家整一下:

<!-- shiro-redis -->
<dependency>
    <groupId>org.crazycake</groupId>
    <artifactId>shiro-redis</artifactId>
    <version>2.4.2.1-RELEASE</version>
</dependency>
复制代码


修改ShiroConfig,添加方法

public RedisManager redisManager() {
    RedisManager redisManager = new RedisManager();
    return redisManager;
}
public RedisCacheManager cacheManager() {
    RedisCacheManager redisCacheManager = new RedisCacheManager();
    redisCacheManager.setRedisManager(redisManager());
    return redisCacheManager;
}
@Bean
public SecurityManager securityManager(){
    DefaultWebSecurityManager securityManager =  new DefaultWebSecurityManager();
    securityManager.setRealm(shiroRealm());
    securityManager.setRememberMeManager(rememberMeManager());
    // 添加缓存
    securityManager.setCacheManager(cacheManager());
    return securityManager;
}
复制代码


这样就可以了,大家可以把测试获取用户的地方改成数据库获取,看下控制台sql日志会明显减少,因为有一部分是从缓存拿的


Session会话管理

这部分功能还是比较好玩的,学完可以自由发挥做一个房间功能,可以加入可以踢人,下面我们就开整


添加SessionDao

修改ShiroConfig,添加方法,因为我们使用的是Redis缓存

@Bean
public RedisSessionDAO sessionDAO() {
    RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
    redisSessionDAO.setRedisManager(redisManager());
    return redisSessionDAO;
}
@Bean
public SessionManager sessionManager() {
    DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
    Collection<SessionListener> listeners = new ArrayList<SessionListener>();
    listeners.add(new ShiroSessionListener());
    sessionManager.setSessionListeners(listeners);
    sessionManager.setSessionDAO(sessionDAO());
    return sessionManager;
}
复制代码


实现SessionListener

public class ShiroSessionListener implements SessionListener {
    // 原子值,可以维护用户数
    private final AtomicInteger sessionCount = new AtomicInteger(0);
    // 建立时
    @Override
    public void onStart(Session session) {
        sessionCount.incrementAndGet();
    }
    // 停止时
    @Override
    public void onStop(Session session) {
        sessionCount.decrementAndGet();
    }
    // 过期时
    @Override
    public void onExpiration(Session session) {
        sessionCount.decrementAndGet();
    }
}
复制代码


最后同样的,想要开启需要我们注入到Manager中:

@Bean
public SecurityManager securityManager(){
    DefaultWebSecurityManager securityManager =  new DefaultWebSecurityManager();
    securityManager.setRealm(shiroRealm());
    securityManager.setRememberMeManager(rememberMeManager());
    securityManager.setCacheManager(cacheManager());
    // 注入session manager
    securityManager.setSessionManager(sessionManager());
    return securityManager;
}
复制代码


获取在线用户

我们先定义一个类,用来记录在线用户:

@Data
public class UserOnline implements Serializable {
    private static final long serialVersionUID = 3828664348416633856L;
    private String sessionId;
    private String userId;
    private String username;
    private String host;
    private String ip;
    private String status;
    private Date startTime;
    private Date lastTime;
    private Long timeout;
}
复制代码


那么怎么获取呢?我们定义一个方法,大家实践中可以抽到Service层,这里方便演示,我直接写到控制器里

@RestController
public class IndexController {
    @Autowired
    private SessionDAO sessionDAO;
    @RequiresPermissions("p:user")
    @RequestMapping("/index")
    public String index(Model model) {
        // 登录成后,即可通过Subject获取登录的用户信息
        User user = (User) SecurityUtils.getSubject().getPrincipal();
        model.addAttribute("user", user);
        return "index --->" + user.getUsername();
    }
    @RequiresPermissions("p:admin")
    @RequestMapping("/userOnline/list")
    @ResponseBody
    public List<UserOnline> list() {
        List<UserOnline> list = new ArrayList<>();
        Collection<Session> sessions = sessionDAO.getActiveSessions();
        for (Session session : sessions) {
            UserOnline userOnline = new UserOnline();
            User user = new User();
            SimplePrincipalCollection principalCollection;
            if (session.getAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY) == null) {
                continue;
            } else {
                principalCollection = (SimplePrincipalCollection) session
                        .getAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY);
                user = (User) principalCollection.getPrimaryPrincipal();
                userOnline.setUsername(user.getUsername());
                userOnline.setUserId(user.getId().toString());
            }
            userOnline.setSessionId((String) session.getId());
            userOnline.setHost(session.getHost());
            userOnline.setStartTime(session.getStartTimestamp());
            userOnline.setLastTime(session.getLastAccessTime());
            Long timeout = session.getTimeout();
            if (timeout == 0l) {
                userOnline.setStatus("离线");
            } else {
                userOnline.setStatus("在线");
            }
            userOnline.setTimeout(timeout);
            list.add(userOnline);
        }
        return list;
    }
}
复制代码


强制用户下线

如果你看谁不爽,可以直接让他下线,hhh~

@RequiresPermissions("p:admin")
@RequestMapping("/forceLogout")
@ResponseBody
public boolean forceLogout(String sessionId) {
    Session session = sessionDAO.readSession(sessionId);
    session.setTimeout(0);
    return true;
}
复制代码

是不是很简单,这里就不演示了,大家自行试试


结束语

本期内容就到这里结束了,总结一下,本节主要讲了Shiro如何进行缓存以及如何进行用户会话管理,大家可以举一反三,做一些小功能尝试尝试


下期预告

下期给大家讲讲Shiro中如何整合JWT,这个大家应该不陌生,如果不知道啥是JWT也没关系,我会带大家一步一步入门,下期也是Shiro系列的终极篇,内容可能有点多,耐心看完哦。欢迎加群一起学习交流 ~

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
15天前
|
Java API 数据库
构建RESTful API已经成为现代Web开发的标准做法之一。Spring Boot框架因其简洁的配置、快速的启动特性及丰富的功能集而备受开发者青睐。
【10月更文挑战第11天】本文介绍如何使用Spring Boot构建在线图书管理系统的RESTful API。通过创建Spring Boot项目,定义`Book`实体类、`BookRepository`接口和`BookService`服务类,最后实现`BookController`控制器来处理HTTP请求,展示了从基础环境搭建到API测试的完整过程。
31 4
|
17天前
|
JavaScript 安全 Java
如何使用 Spring Boot 和 Ant Design Pro Vue 实现动态路由和菜单功能,快速搭建前后端分离的应用框架
本文介绍了如何使用 Spring Boot 和 Ant Design Pro Vue 实现动态路由和菜单功能,快速搭建前后端分离的应用框架。首先,确保开发环境已安装必要的工具,然后创建并配置 Spring Boot 项目,包括添加依赖和配置 Spring Security。接着,创建后端 API 和前端项目,配置动态路由和菜单。最后,运行项目并分享实践心得,包括版本兼容性、安全性、性能调优等方面。
103 1
|
12天前
|
Java API 数据库
Spring Boot框架因其简洁的配置、快速的启动特性及丰富的功能集而备受开发者青睐
本文通过在线图书管理系统案例,详细介绍如何使用Spring Boot构建RESTful API。从项目基础环境搭建、实体类与数据访问层定义,到业务逻辑实现和控制器编写,逐步展示了Spring Boot的简洁配置和强大功能。最后,通过Postman测试API,并介绍了如何添加安全性和异常处理,确保API的稳定性和安全性。
27 0
|
1天前
|
JavaScript 安全 Java
如何使用 Spring Boot 和 Ant Design Pro Vue 构建一个具有动态路由和菜单功能的前后端分离应用。
本文介绍了如何使用 Spring Boot 和 Ant Design Pro Vue 构建一个具有动态路由和菜单功能的前后端分离应用。首先,创建并配置 Spring Boot 项目,实现后端 API;然后,使用 Ant Design Pro Vue 创建前端项目,配置动态路由和菜单。通过具体案例,展示了如何快速搭建高效、易维护的项目框架。
77 62
|
3天前
|
JSON Java API
springboot集成ElasticSearch使用completion实现补全功能
springboot集成ElasticSearch使用completion实现补全功能
14 1
|
18天前
|
JavaScript 安全 Java
如何使用 Spring Boot 和 Ant Design Pro Vue 构建一个具有动态路由和菜单功能的前后端分离应用
【10月更文挑战第8天】本文介绍了如何使用 Spring Boot 和 Ant Design Pro Vue 构建一个具有动态路由和菜单功能的前后端分离应用。首先,通过 Spring Initializr 创建并配置 Spring Boot 项目,实现后端 API 和安全配置。接着,使用 Ant Design Pro Vue 脚手架创建前端项目,配置动态路由和菜单,并创建相应的页面组件。最后,通过具体实践心得,分享了版本兼容性、安全性、性能调优等注意事项,帮助读者快速搭建高效且易维护的应用框架。
25 3
|
7天前
|
存储 Java 数据管理
强大!用 @Audited 注解增强 Spring Boot 应用,打造健壮的数据审计功能
本文深入介绍了如何在Spring Boot应用中使用`@Audited`注解和`spring-data-envers`实现数据审计功能,涵盖从添加依赖、配置实体类到查询审计数据的具体步骤,助力开发人员构建更加透明、合规的应用系统。
|
26天前
|
存储 缓存 NoSQL
数据的存储--Redis缓存存储(一)
数据的存储--Redis缓存存储(一)
60 1
|
26天前
|
存储 缓存 NoSQL
数据的存储--Redis缓存存储(二)
数据的存储--Redis缓存存储(二)
37 2
数据的存储--Redis缓存存储(二)
|
22天前
|
消息中间件 缓存 NoSQL
Redis 是一个高性能的键值对存储系统,常用于缓存、消息队列和会话管理等场景。
【10月更文挑战第4天】Redis 是一个高性能的键值对存储系统,常用于缓存、消息队列和会话管理等场景。随着数据增长,有时需要将 Redis 数据导出以进行分析、备份或迁移。本文详细介绍几种导出方法:1)使用 Redis 命令与重定向;2)利用 Redis 的 RDB 和 AOF 持久化功能;3)借助第三方工具如 `redis-dump`。每种方法均附有示例代码,帮助你轻松完成数据导出任务。无论数据量大小,总有一款适合你。
56 6