Talk about the design idea of kicking people offline! (with code implementation)

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 Sessionmode, 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 Sessiondoes 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

  1. In the login interface, we need to accept two parameters: username + password, take these two parameters to get data from the database
  2. If the data cannot be found, return directly 用户名或密码错误, if the data can be found, then start to log in
  3. Use a certain algorithm (such as uuid) to generate a random string, like this: 623368f0-ae5e-4475-a53f-93e4225f16ae, this is our token
  4. Now we need to do two things, one is to establish the mapping relationship between this tokenand UserIdthe other, and the other is to tokenreturn this to the front end
    1. Create a mapping: Redisadd a piece of data in, if userId=10001, then we needRedisUtil.set("623368f0-ae5e-4475-a53f-93e4225f16ae", 10001)
    2. It will tokenbe passed to the front desk, you can put it Cookiein, or directly put 返回体bodyit in
  5. 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-93e4225f16aethis token is the one 用户10001!
  6. A session access comes in, and tokenthe 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:

  1. This client holdstoken
  2. This tokenis an effective token, that is: you Rediscan 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 tokenandUserId

You might be thinking, isn't that easy? RedisClearing 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 Redisstore token -> UserIdthe mapping relationship in , if we want to kick out the user 10001, under normal circumstances, we can't just find out which key value 10001it corresponds totoken

To solve this problem, we must UserId -> tokenalso 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:

  1. Find the 账号10001corresponding tokenkey value
  2. delete this key

OK, kicked out successfully. When this account accesses the system next time, although he has carried it with him token, it tokenhas already become an 无效tokenaccount. 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 jwtwhen 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 jwtit 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 tokencan 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模式, jwtthe controllability of tokens is much weaker, and it is impossible to actively clear token -> UserIdthe mapping relationship

Unless you manually change jwtthe 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 UserIdor into a blacklist, and then we check whether each request's or exists in this blacklist jwt-tokenin the interceptor !tokenUserId

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.

  1. 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>
  1. 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 "登录失败";
	}
}
  1. 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!





Guess you like

Origin blog.csdn.net/shengzhang_/article/details/112915085