前言

前两天写了一篇文章,主要讲了下java中如何实现踢人下线,原文链接:java中如何踢人下线?封禁某个账号后使其会话立即掉线!

本来只是简单阐述一下踢人下线的业务场景和实现方案,没想到引出那么多大佬把小弟喷的睁不开眼睛,为了避免大家继续喷我,特再写下此篇文章,彻底讲清楚各种场景下踢人下线的设计思路,如有不足之处还请各位大佬轻喷!

好了废话不多说,正文开始

正文

如果把踢人下线比喻成拆房子,那么在学会拆房之前,我们必须要了解这座房子是怎么盖起来的,不同的盖法对应不同的拆法,不能混为一谈

对于目前大多数系统来讲,登录主要有两种方式,一是传统Session模式,二是jwt令牌模式

传统Session模式

我们先以Session模式为例,这种模式是怎么登录的呢?

(注:此处的Session不单指HttpSession,指一切使用服务端控制会话的手段)

这里我们不使用任何框架,从底层逻辑开始说起。

首先,你需要一个全局拦截器,拦截所有会话请求,如果此会话已经登录,那么拦截器放行,如果未登录,直接将此会话强制重定向到登录接口

在登录接口,我们需要接受两个参数:username + password, 拿这两个参数去数据库中获取数据

如果查不到数据,直接返回用户名或密码错误,如果可以查找到数据,那么开始登录

利用一定的算法(例如uuid),生成一个随机字符串,就像这样子:623368f0-ae5e-4475-a53f-93e4225f16ae, 这就是我们的token

现在我们需要做两件事,一是建立此token与UserId的映射关系,二是把这个token返回给前端

建立映射:在Redis中添加一条数据,假如userId=10001,那么我们需要RedisUtil.set("623368f0-ae5e-4475-a53f-93e4225f16ae", 10001)

将token传递给前台,你可以放到Cookie里,或者直接放到返回体body里

大工告成,会话登录完毕!在全局拦截器里,我们不认userId只认token,谁持有623368f0-ae5e-4475-a53f-93e4225f16ae这个令牌,谁就是用户10001!

一个会话访问进来,有token且token有效,那么会话放行!没有?乖乖滚去登录!

此时不难看出,一个客户端要保持会话登录的两个必要条件:

此客户端持有token

这个token是一个有效token,即:可以从Redis中找到对应的UserId

而我们要做踢人下线,就必须从这两点至少选择其一开始下手。

首先我们先明确一点:除非客户端主动注销,否则我们是无法清除一个已经颁发到客户端的token的。

(除了Cookie清除技术和WebSocket实时推送技术可以做到,但是这两种技术都需要客户端主动配合,我们现在的假设是客户端拒不配合,我们需要将它强制清退下线。)

现在,我们只能从第二点下手,即:清除此token与UserId的映射关系

你可能会想,这不简单?Redis清除一个键值,还不是一行代码就能解决的事情?

此时你可能漏掉了关键的一点,那就是,我们只在Redis中存储了token -> UserId的映射关系,如果我们要踢出用户10001,正常情况下,我们无法只根据10001找到它对应的token是哪个键值

要解决这个问题,我们就必须把UserId -> token的映射关系也存储一份,你可以存储在数据库中,也可以存储在Redis中,为了性能考虑,我们使用Redis

现在事情变得简单起来,要踢人下线,我们只需要两步:

找到账号10001对应的token键值

删除这个键值

OK,踢出成功,待到此账号下一次访问系统时,虽然他携带了token,但是此token已成为无效token,乖乖去登陆吧!

此时你可能会说:

就这?我创建个集合保存所有要踢出下线的账号,每次拦截器里判断这个会话是否在这个集合中不就OK了?

大佬请慢喷!这就是我要说的第二种模式————黑名单机制,且往下看

jwt模式

jwt模式的登陆步骤与传统Session模式区别不大,在此暂不赘述

不同点在于,jwt登陆时,不会在服务器保存任何会话信息,所有的用户参数都被写进了jwt生成的token中

(所以jwt的token才会长的那么长!通常两三百字符长度起步)

一个会话是否有效,只看这个会话携带的token能不能正常解析出数据!

这也就意味着令牌的合法性是令牌自解释的,而不是服务器说了算!

所以,相比于传统Session模式,jwt对令牌的可控性就弱了很多,无法做到主动清除token -> UserId 映射关系的操作

除非你手动更换jwt令牌生成的算法秘钥,但是这样会造成系统中所有令牌全部失效,全部用户集体下线!这是万万不行的。

那怎么办?难道我就不能做到踢人下线的操作吗?

其实办法肯定是有的,只要思想不滑坡,方法总比困难多!

那就是利用黑名单机制:我们要踢出哪个用户,只需要将他的UserId或者jwt-token放进一个黑名单里,然后我们在拦截器里检查每个请求的token或者UserId是否存在于这个黑名单里即可!

这种方式和传统Session模式孰优孰劣呢?只能说各有千秋!

黑名单机制在存储时节省性能,在拦截器里多了一步黑名单检测的步骤,浪费性能!

不过坦白了讲,这丁点的性能的浪费对于现在的CPU来说都是毛毛雨,可以直接忽略!

题外话

在我一位同事的项目中,给我提供了jwt踢人下线的另一种实现思路:

那就是在生成jwt令牌时,加入一个固定的参数当做令牌生成因子,如果要将一个用户踢出下线,只需要修改一下这个因子的值,然后在拦截器里每次校验这个因子生成的令牌是否与客户端传递的令牌一致!即可判断出这个token是否已被拉黑!

这种模式提供了一个比较新颖的逻辑算法,但是严格来讲,还是借助服务器存储一定的数据完成的会话验证,仍然属于Session模式。在此暂不展开细讲。

代码实现方案?

说了这么多理论,总归是要上代码的,由于笔者除了sa-token框架以外没有找到任何一个框架对踢人下线有直接现成的解决方案,所以在此暂以sa-token框架为例

首先添加pom.xml依赖

cn.dev33

sa-token-spring-boot-starter

1.12.1

在用户登录时将账号id写入会话中

@RestController

@RequestMapping("user")

public class UserController {

@RequestMapping("doLogin")

public String doLogin(String username, String password) {

// 此处仅作示例模拟,真实项目需要从数据库中查询数据进行比对

if("zhang".equals(username) && "123456".equals(password)) {

StpUtil.setLoginId(10001);

return "登录成功";

}

return "登录失败";

}

}

将指定id的账号踢出在线

// 使指定id账号的会话注销登录,对方再次访问系统时会抛出`NotLoginException`异常,场景值为-5

@RequestMapping("kickout")

public String kickout(long userId) {

StpUtil.logoutByLoginId(userId);

return "踢出成功";

}

后话

文章写的再详细也难免会有遗漏之处,在此还求大家轻喷,可以在评论出留言指出不足之处

如果觉得文章写得不错还请大家不要吝惜为文章点个赞,您的支持是我更新的最大动力!

php实现踢下线,浅谈踢人下线的设计思路!(附代码实现方案)相关推荐

  1. java实现踢下线用户_浅谈踢人下线的设计思路!(附代码实现方案)

    前言 前两天写了一篇文章,主要讲了下java中如何实现踢人下线,原文连接:java中如何踢人下线?封禁某个帐号后使其会话当即掉线!前端 原本只是简单阐述一下踢人下线的业务场景和实现方案,没想到引出那么 ...

  2. java web怎么实现踢人_【Java】浅谈踢人下线的设计思路!(附代码实现方案)

    前言 前两天写了一篇文章,主要讲了下java中如何实现踢人下线,原文链接:java中如何踢人下线?封禁某个账号后使其会话立即掉线! 本来只是简单阐述一下踢人下线的业务场景和实现方案,没想到引出那么多大 ...

  3. 浅谈管理系统操作日志设计(附操作日志类)

    原文地址:http://www.cnblogs.com/hooray/archive/2012/09/05/2672133.html 相关文章链接:<系统操作日志设计> 在开始做之前,必须 ...

  4. 浅谈微端游戏设计思路

    其实很多游戏之所以容量大,主要是因为资源的关系,资源太多,造成容量庞大.为啥现在很多3D游戏容量这么大?主要是资源量太多造成容量越来越庞大,而游戏内容不见得比其他游戏多多少.例如3D游戏,可能同样的模 ...

  5. php 如何设计索引_Mysql学习浅谈mysql的索引设计原则以及常见索引的区别

    <Mysql学习浅谈mysql的索引设计原则以及常见索引的区别>要点: 本文介绍了Mysql学习浅谈mysql的索引设计原则以及常见索引的区别,希望对您有用.如果有疑问,可以联系我们. 索 ...

  6. 浅谈Hybrid技术的设计与实现【转】

    https://www.cnblogs.com/yexiaochai/p/4921635.html 前言 浅谈Hybrid技术的设计与实现 浅谈Hybrid技术的设计与实现第二弹 浅谈Hybrid技术 ...

  7. 浅谈Hybrid技术的设计与实现第二弹

    前言 浅谈Hybrid技术的设计与实现 浅谈Hybrid技术的设计与实现第二弹 浅谈Hybrid技术的设计与实现第三弹--落地篇 接上文:浅谈Hybrid技术的设计与实现(阅读本文前,建议阅读这个先) ...

  8. 微型计算机在机械设计中的应用,浅谈计算机技术在机械设计制造及自动化中的应用.docx...

    浅谈计算机技术在机械设计制造及自动化中的应用 当前科学技术与机械制造与自动化技术相互融合,将多种学科中的复合型技术加以整合,形成综合性的机械设计制造自动化学科.作为机械制造的核心内容,自动化在人们的生 ...

  9. 浅谈网站的logo设计

    浅谈网站的logo设计 当今是信息高速发达的时代,互联网实现了世界范围的网络间的互联和信息共享,并已全面介入人类生活的方方面面,带动着人类社会的飞速发展,发挥着重要的作用. 随着PC和网络的普及,上网 ...

最新文章

  1. 如何实践AI深度学习的十大惊艳案例
  2. 解题:HEOI 2016 求和
  3. IP、子网的详述 ——IP分类、网关地址,子网掩码、子网作用
  4. 《机器学习实战》总结
  5. open ai gpt_您实际上想尝试的GPT-3 AI发明鸡尾酒
  6. python异常值处理实例_利用Python进行异常值分析实例代码
  7. oracle 12c 创建PDB用户即Local User (PDB与CDB)
  8. IT项目范围管理案例分析——柳工错在哪里?
  9. 美国重金投资3D芯片项目!MIT+美独资公司攻关,旨在继续领先中国
  10. 程序员更像艺术家 哪种状态更具创造性?
  11. 安装最新版本的PHPUnit后,不能使用
  12. 蓝桥杯c语言基础试题答案,2014年蓝桥杯c语言试题及答案
  13. 在线c语言编程网站_学编程有哪些好的网站推荐?
  14. amd cpu排行_AMD R5系列处理器性能排名 CPU天梯图2017年4月最新完整版
  15. 扎根黄金赛道,尚未盈利的捍宇医疗如何遨游行业蓝海?
  16. MMO 游戏中使用多核
  17. UnicodeEncodeError: ‘charmap‘ codec can‘t encode characters in position 0-1: character maps to <unde
  18. Udesk即时通讯网页插件发送咨询对象(一、使用内嵌代码)
  19. 科研tips——论文图的一些要求
  20. matlab 1向量组,matlab-线性代数 rank 向量组的秩

热门文章

  1. Debian 安装与设置
  2. 云SIM带来的革命?(云simesim)
  3. Category 类别 -Objective-C
  4. 2022年茶艺师(中级)考试题库及茶艺师(中级)找解析
  5. anaconda:一直处于adding featured channel状态
  6. 视频文本检索之CLIP4Clip
  7. 网站统计 开源 java_开源 java CMS - FreeCMS2.8 统计分析
  8. uni-app中兴趣标签选择
  9. Python实现发送邮件(实现单发/群发邮件验证码)
  10. JDK的安装配置 - Windows(结尾附视频)