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