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

本文涉及的产品
云数据库 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
相关文章
|
17天前
|
缓存 NoSQL Java
SpringBoot实现缓存预热的几种常用方案
SpringBoot实现缓存预热的几种常用方案
|
1月前
|
JavaScript 前端开发 Java
基于SpringBoot+Vue实现前后端交互功能(详解Vue框架机制)
基于SpringBoot+Vue实现前后端交互功能(详解Vue框架机制)
|
1月前
|
Java 测试技术 数据库
基于SpringBoot+HTML实现登录注册功能模块
基于SpringBoot+HTML实现登录注册功能模块
|
2月前
|
存储 XML 缓存
【深入浅出Spring原理及实战】「缓存Cache开发系列」带你深入分析Spring所提供的缓存Cache功能的开发实战指南(一)
【深入浅出Spring原理及实战】「缓存Cache开发系列」带你深入分析Spring所提供的缓存Cache功能的开发实战指南
103 0
|
12天前
|
XML 存储 缓存
Spring缓存是如何实现的?如何扩展使其支持过期删除功能?
总之,Spring的缓存抽象提供了一种方便的方式来实现缓存功能,并且可以与各种缓存提供商集成以支持不同的过期策略。您可以根据项目的具体需求选择适合的方式来配置和扩展Spring缓存功能。
16 0
|
17天前
|
缓存 NoSQL Java
Springboot 多级缓存设计与实现
Springboot 多级缓存设计与实现
|
1月前
|
Java 容器
SpringBoot使用配置注解开启自动配置功能&整合spring-boot-configuration-processor
SpringBoot使用配置注解开启自动配置功能&整合spring-boot-configuration-processor
17 0
|
2月前
|
缓存 NoSQL Java
【深入浅出Spring原理及实战】「缓存Cache开发系列」带你深入分析Spring所提供的缓存Cache功能的开发实战指南(二)
【深入浅出Spring原理及实战】「缓存Cache开发系列」带你深入分析Spring所提供的缓存Cache功能的开发实战指南
37 0
|
2月前
|
前端开发 关系型数据库 MySQL
springboot+jpa+tymeleaf实现分页功能
springboot+jpa+tymeleaf实现分页功能
12 0
|
2月前
|
安全 算法 Java
SpringBoot+JWT+Shiro+MybatisPlus实现Restful快速开发后端脚手架
SpringBoot+JWT+Shiro+MybatisPlus实现Restful快速开发后端脚手架
29 0