foreword
I wrote an article two days ago, mainly talking about how to kick people offline in java, the original text link: How to kick people offline in java? After banning an account, its session will be dropped immediately!
Originally, I just briefly explained the business scenario and implementation plan of kicking people offline, but I didn’t expect to attract so many big bosses that I couldn’t open my eyes. In order to prevent everyone from continuing to criticize me, I wrote this article again to thoroughly explain all aspects. The design idea of kicking people offline in this scenario, if there are any deficiencies, please let me know!
Okay, enough nonsense, the text begins
text
If kicking someone offline is compared to demolition of a house, then before learning how to demolish a house, we must understand how the house is built. Different construction methods correspond to different demolition methods, which cannot be confused
For most systems at present, there are two main ways to log in, one is the traditional Session
mode, and the other is jwt令牌
the mode
Traditional Session mode
Let's take it Session模式
as an example, how to log in in this mode?
(Note: This Session
does not only refer to HttpSession
, but refers to all means of using the server to control the session)
Here we don't use any framework, let's start with the underlying logic.
First of all, you need a global interceptor to intercept all session requests. If the session is already logged in, the interceptor will let it go. If not logged in, directly redirect the session to the login interface forcibly
- In the login interface, we need to accept two parameters:
username + password
, take these two parameters to get data from the database - If the data cannot be found, return directly
用户名或密码错误
, if the data can be found, then start to log in - Use a certain algorithm (such as uuid) to generate a random string, like this:
623368f0-ae5e-4475-a53f-93e4225f16ae
, this is our token - Now we need to do two things, one is to establish the mapping relationship between this
token
andUserId
the other, and the other is totoken
return this to the front end- Create a mapping:
Redis
add a piece of data in, ifuserId=10001
, then we needRedisUtil.set("623368f0-ae5e-4475-a53f-93e4225f16ae", 10001)
- It will
token
be passed to the front desk, you can put itCookie
in, or directly put返回体body
it in
- Create a mapping:
- You're done, session login is complete! In the global interceptor, we do not recognize the userId but only the token. Whoever holds
623368f0-ae5e-4475-a53f-93e4225f16ae
this token is the one用户10001
! - A session access comes in, and
token
the token is valid, then the session is released! No? Obediently go log in!
At this point, it is not difficult to see that there are two necessary conditions for a client to maintain a session login:
- This client holds
token
- This
token
is an effectivetoken
, that is: youRedis
can find the correspondingUserId
And if we want to kick people offline, we must choose at least one of these two points to start .
First of all, let's make it clear: unless the client actively logs out, we cannot clear a token that has been issued to the client.
(Except Cookie清除技术
and WebSocket实时推送技术
can be done, but these two technologies require the active cooperation of the client. Our current assumption is that the client refuses to cooperate, and we need to force it to log off the line.)
Now, we can only start from the second point, namely: clear the mapping relationship between this token
andUserId
You might be thinking, isn't that easy? Redis
Clearing a key value is not something that can be solved with one line of code?
At this point you may have missed the key point, that is, we only Redis
store token -> UserId
the mapping relationship in , if we want to kick out the user 10001
, under normal circumstances, we can't just find out which key value 10001
it corresponds totoken
To solve this problem, we must UserId -> token
also store a copy of the mapping relationship. You can store it in the database or in it Redis
. For performance considerations, we use Redis
Now things are easier, to kick someone offline, we only need two steps:
- Find the
账号10001
correspondingtoken
key value - delete this key
OK, kicked out successfully. When this account accesses the system next time, although he has carried it with him token
, it token
has already become an 无效token
account. Go and log in obediently!
At this point you might say:
That's it? I create a collection to save all the accounts that need to be kicked offline. Isn’t it OK to judge whether the session is in this collection every time in the interceptor?
Boss, please slow down! This is the second mode I want to talk about - the blacklist mechanism, and look down
jwt mode
jwt模式
There is not much difference between the login steps and the login steps 传统Session模式
, so I won’t repeat them here
The difference is that jwt
when logging in, no session information will be saved on the server, and all user parameters are written into the token generated by jwt
(That's why jwt
it token
's so long! Usually it starts with two or three hundred characters)
Whether a session is valid depends on whether the data carried by the session token
can be parsed normally!
This also means that the legitimacy of the token is self-explanatory by the token, not by the server!
Therefore, in comparison 传统Session模式
, jwt
the controllability of tokens is much weaker, and it is impossible to actively clear token -> UserId
the mapping relationship
Unless you manually change jwt
the algorithm key generated by the token, but this will cause all tokens in the system to become invalid, and all users will go offline collectively! This is absolutely impossible.
So what to do? Can't I do the operation of kicking people offline?
In fact, there must be ways, as long as the thinking does not slip, there are more ways than difficulties!
That is to use the blacklist mechanism: if we want to kick out any user, we only need to put his UserId
or into a blacklist, and then we check whether each request's or exists in this blacklist jwt-token
in the interceptor !token
UserId
Which method is better than the traditional Session mode? It can only be said that each has its own merits!
The blacklist mechanism saves performance during storage, and there is an extra step of blacklist detection in the interceptor, which wastes performance!
But frankly speaking, this bit of performance waste is drizzle for the current CPU, and can be ignored directly!
Digression
In the project of one of my colleagues, I was provided with another implementation idea of jwt kicking people offline:
That is, when generating the jwt token, add a fixed parameter as the token 生成因子
. If you want to kick a user offline, you only need to modify the value of this factor, and then verify the value generated by this factor in the interceptor every time. Whether the token is consistent with the token passed by the client! You can judge whether the token has been blocked!
This mode provides a relatively novel logic algorithm, but strictly speaking, it still belongs to the session verification completed by storing certain data on the server Session模式
. I will not go into details here.
Code implementation?
Having said so many theories, it is necessary to upload the code. Since the author sa-token框架
has not found any framework that has a direct and ready-made solution for kicking people offline, I will take this as an sa-token框架
example for the time being.
- First add pom.xml dependency
<!-- sa-token 权限认证, 在线文档:http://sa-token.dev33.cn/ -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-boot-starter</artifactId>
<version>1.12.1</version>
</dependency>
- Write the account id into the session when the user logs in
@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 "登录失败";
}
}
- Kick the account with the specified id from online
// 使指定id账号的会话注销登录,对方再次访问系统时会抛出`NotLoginException`异常,场景值为-5
@RequestMapping("kickout")
public String kickout(long userId) {
StpUtil.logoutByLoginId(userId);
return "踢出成功";
}
Students who are interested in the framework can check the official website: sa-token A lightweight authority authentication framework for java
Afterword
No matter how detailed the article is written, it is inevitable that there will be omissions. I also ask everyone to lighten up here. You can leave a message in the comments to point out the shortcomings
If you think the article is well written, please don’t hesitate to give it a thumbs up. Your support is my biggest motivation for updating!