点击上方 好好学java ,选择 星标 公众号

重磅资讯、干货,第一时间送达今日推荐:2020年7月程序员工资统计,平均14357元,又跌了,扎心个人原创100W+访问量博客:点击前往,查看更多

作者:殷天文

www.jianshu.com/p/b6f5ec98d790

通常系统都会限制同一个账号的登录人数,多人登录要么限制后者登录,要么踢出前者,Spring Security 提供了这样的功能,本文讲解一下在没有使用Security的时候如何手动实现这个功能

本文借鉴了

https://jinnianshilongnian.iteye.com/blog/2039760

如果你是使用 Shiro + Session 的模式,可以阅读此文

demo 技术选型

  • SpringBoot

  • JWT

  • Filter

  • Redis + Redisson

JWT(token)存储在Redis中,类似 JSessionId-Session的关系,用户登录后每次请求在Header中携带jwt

如果你是使用session的话,也完全可以借鉴本文的思路,只是代码上需要加些改动

两种实现思路

比较时间戳

维护一个 username: jwtToken 这样的一个 key-value 在Reids中, Filter逻辑如下

图片不清可点开放大

public class CompareKickOutFilter extends KickOutFilter {@Autowiredprivate UserService userService;@Overridepublic boolean isAccessAllowed(HttpServletRequest request, HttpServletResponse response) {String token = request.getHeader("Authorization");String username = JWTUtil.getUsername(token);String userKey = PREFIX + username;RBucket<String> bucket = redissonClient.getBucket(userKey);String redisToken = bucket.get();if (token.equals(redisToken)) {return true;} else if (StringUtils.isBlank(redisToken)) {bucket.set(token);} else {Long redisTokenUnixTime = JWTUtil.getClaim(redisToken, "createTime").asLong();Long tokenUnixTime = JWTUtil.getClaim(token, "createTime").asLong();// token > redisToken 则覆盖if (tokenUnixTime.compareTo(redisTokenUnixTime) > 0) {bucket.set(token);} else {// 注销当前tokenuserService.logout(token);sendJsonResponse(response, 4001, "您的账号已在其他设备登录");return false;}}return true;}
}

队列踢出

public class QueueKickOutFilter extends KickOutFilter {/*** 踢出之前登录的/之后登录的用户 默认踢出之前登录的用户*/private boolean kickoutAfter = false;/*** 同一个帐号最大会话数 默认1*/private int maxSession = 1;public void setKickoutAfter(boolean kickoutAfter) {this.kickoutAfter = kickoutAfter;}public void setMaxSession(int maxSession) {this.maxSession = maxSession;}@Overridepublic boolean isAccessAllowed(HttpServletRequest request, HttpServletResponse response) throws Exception {String token = request.getHeader("Authorization");UserBO currentSession = CurrentUser.get();Assert.notNull(currentSession, "currentSession cannot null");String username = currentSession.getUsername();String userKey = PREFIX + "deque_" + username;String lockKey = PREFIX_LOCK + username;RLock lock = redissonClient.getLock(lockKey);lock.lock(2, TimeUnit.SECONDS);try {RDeque<String> deque = redissonClient.getDeque(userKey);// 如果队列里没有此token,且用户没有被踢出;放入队列if (!deque.contains(token) && currentSession.isKickout() == false) {deque.push(token);}// 如果队列里的sessionId数超出最大会话数,开始踢人while (deque.size() > maxSession) {String kickoutSessionId;if (kickoutAfter) { // 如果踢出后者kickoutSessionId = deque.removeFirst();} else { // 否则踢出前者kickoutSessionId = deque.removeLast();}try {RBucket<UserBO> bucket = redissonClient.getBucket(kickoutSessionId);UserBO kickoutSession = bucket.get();if (kickoutSession != null) {// 设置会话的kickout属性表示踢出了kickoutSession.setKickout(true);bucket.set(kickoutSession);}} catch (Exception e) {}}// 如果被踢出了,直接退出,重定向到踢出后的地址if (currentSession.isKickout()) {// 会话被踢出了try {// 注销userService.logout(token);sendJsonResponse(response, 4001, "您的账号已在其他设备登录");} catch (Exception e) {}return false;}} finally {if (lock.isHeldByCurrentThread()) {lock.unlock();LOGGER.info(Thread.currentThread().getName() + " unlock");} else {LOGGER.info(Thread.currentThread().getName() + " already automatically release lock");}}return true;}}

比较两种方法

第一种方法逻辑简单粗暴, 只维护一个key-value 不需要使用锁,非要说缺点的话没有第二种方法灵活。

第二种方法我很喜欢,代码很优雅灵活,但是逻辑相对麻烦一些,而且为了保证线程安全地操作队列,要使用分布式锁。目前我们项目中使用的是第一种方法

演示

下载地址:

https://gitee.com/yintianwen7/taven-springboot-learning/tree/master/login-control

  • 运行项目,访问localhost:8887 demo中没有存储用户信息,随意输入用户名密码,用户名相同则被踢出

  • 访问 localhost:8887/index.html 弹出用户信息, 代表当前用户有效

  • 另一个浏览器登录相同用户名,回到第一个浏览器刷新页面,提示被踢出

  • application.properties中选择开启哪种过滤器模式,默认是比较时间戳踢出,开启队列踢出 queue-filter.enabled=true

最后,再附上我历时三个月总结的 Java 面试 + Java 后端技术学习指南,笔者这几年及春招的总结,github 1.4k star,拿去不谢!

下载方式1. 首先扫描下方二维码2. 后台回复「Java面试」即可获取

如何用 SpringBoot 实现并发登录人数控制(附代码)相关推荐

  1. SpringBoot 实现并发登录人数控制

    作者丨殷天文 www.jianshu.com/p/b6f5ec98d790 今天跟大家分享SpringBoot 实现并发登录人数控制的知识. 1 SpringBoot 实现并发登录人数控制 通常系统都 ...

  2. springboot + shiro 尝试登录次数限制与并发登录人数控制

    源码项目地址 尝试登录次数控制实现 实现原理 Realm在验证用户身份的时候,要进行密码匹配.最简单的情况就是明文直接匹配,然后就是加密匹配,这里的匹配工作则就是交给CredentialsMatche ...

  3. Shiro并发登录人数控制遇到的问题和解决

    shiro并发登录人数控制遇到的问题和解决 问题1:KickoutSessionControlFilter不起作用 问题2:KickoutSessionControlFilter中cache为null ...

  4. 模仿爱奇艺账号登录限制人数,SpringBoot 并发登录人数控制,踢人功能

    通常系统都会限制同一个账号的登录人数,多人登录要么限制后者登录,要么踢出前者,Spring Security 提供了这样的功能,本文讲解一下在没有使用Security的时候如何手动实现这个功能 技术选 ...

  5. SpringBoot 并发登录人数控制

    点击上方"方志朋",选择"置顶公众号" 技术文章第一时间送达! 作者:殷天文 www.jianshu.com/p/b6f5ec98d790 技术经验交流:点击入 ...

  6. shiro并发登录人数控制

    在某些项目中可能会遇到如每个账户同时只能有一个人登录或几个人同时登录,如果同时有多人登录:要么不让后者登录:要么踢出前者登录(强制退出).比如spring security就直接提供了相应的功能:Sh ...

  7. springboot + shiro之登录人数限制、登录判断重定向、session时间设置

    springboot + shiro之登录人数控制 项目 前篇:spring boot + mybatis + layui + shiro后台权限管理系统:https://blog.51cto.com ...

  8. 厉害了,教你用 Spring Boot 控制并发登录人数

    作者:殷天文 www.jianshu.com/p/b6f5ec98d790 通常系统都会限制同一个账号的登录人数,多人登录要么限制后者登录,要么踢出前者,Spring Security 提供了这样的功 ...

  9. springboot+vue实现登录案例(附VUE整个项目代码)

    文章目录 前言 一.准备环境 1.1 springboot 整合mybatis.swagger.代码生成器.Lombok 1.2 环境工具 二.开发思路及流程 2.1 根据前端登录代码和用户页面设计对 ...

最新文章

  1. 蛋白对接_【分子对接教程】蛋白/核酸/多肽-小分子对接(DOCK 6.9)
  2. windows 7系统搭建PHP网站环境
  3. linux i3wm性能,Core i3-530集成显卡Linux性能考察
  4. 485通讯转换器产品功能特点介绍
  5. python 数据字典用法_python数据字典的操作
  6. 软件测试方法的分类细谈
  7. 最正确的为GridView添加删除提示的方法(转)
  8. bug6-_SymbolicException: Inputs to eager execution function cannot be Keras symbolic
  9. 江民科技召开临时董事会 王江民之子接手管理
  10. 机械优化设计c语言鲍威尔法,机械优化设计鲍威尔法.docx
  11. liferay-protal学习1-配置开发环境
  12. 华为OD 社招(Java后端)一面
  13. DirectX的发展历程!
  14. 怎么用迅捷PDF转换器在线为PDF文件添加文字内容
  15. 程序员如何快速上位当领导?
  16. [首发] 多方位玩转“地平线新发布AIoT开发板——旭日X3派(Sunrise x3 Pi)” 插电!开机!轻松秒杀!
  17. 如何取消shutdown关机命令?-shutdown命令的使用解析
  18. 几种常用的接口协议的积累,欢迎补充
  19. linux9.0安装教程,RedHat Linux 9.0安装过程小记
  20. PASCAL VOC 2012数据集及其增强版介绍

热门文章

  1. c# 水晶报表中处理TextObject
  2. Updater Application Block for .NET
  3. VC MFC界面上显示BMP图片
  4. 让每次编译产生的目标文件都能打印出编译时间信息
  5. misc_register、 register_chrdev 的区别总结
  6. 蓝牙协议分析(6)_BLE地址类型(蜗窝科技)
  7. Python爬取B站弹幕方法介绍
  8. c matlab 混合编程 调试,64位MATLAB和C混合编程以及联合调试
  9. 波卡链Substrate (4)托盘Pallets
  10. java元婴期(28)----java进阶(springmvc(2)---入门程序(下)基于注解开发(重点掌握))