前言
大家好,一直以来我都本着用最通俗的话理解核心的知识点, 我认为所有的难点都离不开 基础知识 的铺垫。目前正在出一个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
系列的终极篇,内容可能有点多,耐心看完哦。欢迎加群一起学习交流 ~