Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

How to realize Code scanning Login based on Java

2025-02-24 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)05/31 Report--

This article mainly introduces the relevant knowledge of how to achieve code scan login based on Java, the content is detailed and easy to understand, the operation is simple and fast, and has a certain reference value. I believe you will have something to gain after reading this article on how to achieve code scan login based on Java. Let's take a look.

Principle analysis 1. Identity authentication mechanism

Before introducing the principle of code scanning login, let's talk about the identity authentication mechanism on the server side. Take the ordinary account + password login method as an example, after receiving the login request from the user, the server first verifies the legitimacy of the account and password. If the authentication passes, the server assigns a token to the user, which is associated with the user's identity information and can be used as the user's login credentials. Later, when the PC sends the request again, it needs to carry the token in the Header or Query parameters of the request, and the server can identify the current user according to the token. The advantage of token is that it is more convenient and secure, it reduces the risk of account password being hijacked, and users do not need to enter account numbers and passwords repeatedly. The process for logging in via account and password on PC is as follows:

Scanning password login is essentially an identity authentication method. The difference between account + password login and scanning code login is that the former uses the account number and password of the PC side to apply for a token for the PC side, while the latter uses the token + device information of the mobile terminal to apply for a token for the PC side. The purpose of these two login methods is the same, in order to enable the PC side to obtain the "authorization" of the server. Before applying for token for the PC side, both of them need to prove their identity to the server, that is, they must let the server know who the current user is, so that the server can generate the PC token for it. Since the mobile phone must be logged in before scanning the code, the mobile phone itself has saved a token, which can be used for server identification. So why do mobile phones still need device information when verifying their identity? In fact, authentication on the mobile side is slightly different from that on the PC side:

The mobile phone also needs to enter the account number and password before logging in, but the login request contains device information in addition to the account password, such as device type, device id and so on.

After receiving the login request, the server validates the account and password, and after verification, the user information is associated with the device information, that is, they are stored in a data structure structure.

The server generates a token for the mobile end, and associates the token with user information and device information, that is, taking token as the key,structure as the value, persisting the key-value pair locally, and then returning the token to the mobile end.

The mobile end sends the request, carries the token and device information, and the server queries out the structure according to the token, and verifies whether the device information in the structure is the same as that of the mobile phone, so as to judge the effectiveness of the user.

After we have successfully logged in on the PC, we can browse the web page normally in a short time, but then we have to log in again when we visit the website. This is because token has an expiration time, and a long valid period will increase the risk of token hijacking. However, this problem seems to be rare on the mobile side. For example, Wechat can be used all the time after a successful login, even if Wechat is closed or the phone is restarted. This is because the device information is unique, even if the token is hijacked, because the device information is different, the attacker can not prove his identity to the server, which greatly improves the security factor, so the token can be used for a long time. The process for the mobile phone to log in through the account password is as follows:

two。 Process Overview

After understanding the identity authentication mechanism of the server, let's talk about the whole process of scanning the code to log in. Take the web version of Wechat as an example, after we click the QR code on the PC side to log in, the browser page will pop up a QR code picture, open the mobile phone Wechat to scan the QR code, and the PC side will display "scanning code". After the mobile side clicks to confirm login, the PC side will display "login successfully".

In the above process, the server can respond to the PC according to the operation of the mobile phone, so how does the server relate the two? The answer is through the "QR code", strictly speaking, through the contents of the QR code. Using the QR code decoder to scan the QR code of the web version of Wechat, you can get the following content:

From the picture above, we know that the QR code actually contains a web address, and after scanning the QR code, the mobile phone will send a request to the server according to the QR code. Next, we open the developer tool for the PC browser:

It can be seen that after the QR code is displayed, the PC side has not been "idle". It constantly sends requests to the server through polling to get the results of the mobile phone operation. Here we notice that the URL sent by the PC side has a parameter uuid with a value of "Adv-NP1FYw==", and the uuid also exists in the URL contained in the QR code. From this we can infer that the server will become a QR code id before generating the QR code, and the QR code id will be bound with the status and expiration time of the QR code and stored on the server side. The mobile phone can operate the status of the server's QR code according to the QR code id, and the PC can inquire the server about the QR code status according to the QR code id.

At first, the QR code is "to be scanned". After scanning the code, the server changes its status to "to be confirmed". When the polling request of the PC arrives, the server returns a "to be confirmed" response to it. After the mobile phone confirms the login, the QR code becomes "confirmed". When the server generates the token,PC side for identity authentication for the PC side, you can get the token. The whole process of scanning the login code is shown as follows:

The PC side sends a "scan login" request, and the server generates the QR code id, and stores the expiration time, status and other information of the QR code.

The PC side acquires the QR code and displays it.

The PC side starts polling to check the status of the QR code, which is initially "to be scanned".

The mobile phone scans the QR code to get the QR code id.

The mobile phone sends a "code scan" request to the server, which contains the QR code id, mobile token and device information.

The server verifies the validity of the mobile end user, sets the status of the QR code to "to be confirmed" after the verification, and associates the user information with the QR code, and then generates an one-time token for the mobile end, which is used as a certificate to confirm login.

When polling on the PC side, the QR code status is "to be confirmed".

The mobile phone sends a "confirm login" request to the server, which carries the QR code id, disposable token and device information.

The server verifies the one-time token, sets the QR code status to "confirmed" after passing the verification, and generates the PC token for the PC side.

When polling on the PC side, the status of the QR code is "confirmed", and the token on the PC side is obtained, and then the PC side no longer polls.

The PC side accesses the server side through the PC side token.

In the above process, we noticed that after scanning the code on the mobile phone, the server will return an one-time token, and the token is also an identity certificate, but it can only be used once. The purpose of an one-time token is to ensure that the "scan code request" and the "confirm login" request are sent from the same mobile end, that is, mobile users cannot "confirm login for other users".

I don't know much about disposable token, but it can be inferred that in the server cache, the value of disposable token mapping should contain the QR code information, device information and user information passed in by the "scan code" request.

Code implementation 1. Environmental preparation

JDK 1.8: the project is written in the Java language.

Maven: dependency management.

Redis:Redis not only stores the user's identity information as a database (MySQL is not used to simplify the operation), but also stores QR code information, token information and so on.

two。 Mainly rely on

SpringBoot: basic environment of the project.

Hutool: an open source tool class in which QrCodeUtil can be used to generate QR code images.

Thymeleaf: template engine for page rendering.

3. Generate a QR code

The generation of the QR code and the storage logic of the QR code state are as follows:

When the PC side accesses the login request, the server calls the createQrImg method to generate a uuid and a LoginTicket object, which encapsulates the user's userId and QR code status in the LoginTicket object. Then the server stores uuid as a key,LoginTicket object in the Redis server as a value, and sets the valid time to 5 minutes (the valid time of the QR code). The logic of the createQrImg method is as follows:

Public String createQrImg () {/ / uuid String uuid = CommonUtil.generateUUID (); LoginTicket loginTicket = new LoginTicket (); / / the QR code was originally WAITING status loginTicket.setStatus (QrCodeStatusEnum.WAITING.getStatus ()); / / stored in redis String ticketKey = CommonUtil.buildTicketKey (uuid); cacheStore.put (ticketKey, loginTicket, LoginConstant.WAIT_EXPIRED_SECONDS, TimeUnit.SECONDS); return uuid;}

We mentioned in the previous section that the operation of the mobile phone mainly affects the status of the QR code, and the PC side also checks the status of the QR code when polling, so why encapsulate userId in the LoginTicket object? This is to associate the QR code with the user. Imagine logging in to the web version of Wechat. After scanning the code on the mobile phone, the PC side will display the user's avatar. Although the mobile end has not confirmed the login, the PC side has already obtained the current code-scanned user (only the profile photo information). Therefore, after scanning the code on the mobile phone, the QR code needs to be bound with the user, and the use of LoginTicket object is only one way of implementation. After the QR code is generated, we set its status to "to be scanned". UserId does not process it, and defaults to null.

4. scan a QR code

When the mobile phone sends the "scan code" request, the uuid is carried in the Query parameter. After receiving the request, the server calls the scanQrCodeImg method, queries the QR code according to the uuid and sets its status to "waiting for confirmation". After the operation is completed, the server returns the information of "successful scan" or "invalid QR code" to the mobile phone:

@ RequestMapping (path = "/ scan", method = RequestMethod.POST) @ ResponseBodypublic Response scanQrCodeImg (@ RequestParam String uuid) {JSONObject data = loginService.scanQrCodeImg (uuid); if (data.getBoolean ("valid")) {return Response.createResponse ("code successful", data);} return Response.createErrorResponse ("QR code is invalid");}

The main logic of the scanQrCodeImg method is as follows:

Public JSONObject scanQrCodeImg (String uuid) {/ / prevent multiple mobile terminals from scanning the same QR code lock.lock (); JSONObject data = new JSONObject (); try {String ticketKey = CommonUtil.buildTicketKey (uuid); LoginTicket loginTicket = (LoginTicket) cacheStore.get (ticketKey); / / Long expired = cacheStore.getExpireForSeconds (ticketKey) may not be deleted immediately after key expires in redis Boolean valid = loginTicket! = null & & QrCodeStatusEnum.parse (loginTicket.getStatus ()) = = QrCodeStatusEnum.WAITING & & expired! = null & & expired > = 0; if (valid) {User user = hostHolder.getUser (); if (user = = null) {throw new RuntimeException ("user not logged in") } / / modify scan status loginTicket.setStatus (QrCodeStatusEnum.SCANNED.getStatus ()); Condition condition = CONDITION_CONTAINER.get (uuid); if (condition! = null) {condition.signal (); CONDITION_CONTAINER.remove (uuid) } / / Associate the QR code with the user loginTicket.setUserId (user.getUserId ()); cacheStore.put (ticketKey, loginTicket, expired, TimeUnit.SECONDS); / / generate an one-time token for subsequent confirmation requests String onceToken = CommonUtil.generateUUID () CacheStore.put (CommonUtil.buildOnceTokenKey (onceToken), uuid, LoginConstant.ONCE_TOKEN_EXPIRE_TIME, TimeUnit.SECONDS); data.put ("once_token", onceToken);} data.put ("valid", valid); return data;} finally {lock.unlock ();}}

1. First, query the LoginTicket object stored in Redis according to uuid, and then check whether the state of the QR code is "to be scanned". If so, change the state of the QR code to "to be confirmed". If not, the QR code has been scanned and the server prompts the user that the QR code is invalid. We stipulate that only the first mobile phone can be scanned successfully, and the purpose of locking is to ensure the atomicity of the query + modification operation, to avoid scanning the code at the same time, and to detect the status of the QR code as "to be scanned" at the same time.

two。 After the previous step is successful, the server sets the userId in the LoginTicket object to the userId of the current user (code-scanning user), that is, binding the QR code to the user information. Since the code scan request is sent by the mobile phone, the request must come from a valid user. We configure an interceptor (or filter) in the project. After intercepting the "code scan" request, we query the user information according to the token in the request (the mobile phone must carry token when sending the request) and store it in the ThreadLocal container (hostHolder). The user information can then be extracted from the ThreadLocal container when the information is bound. Note that the token refers to the mobile token. In practice, there should be device information, but in order to simplify the operation, we ignore the device information.

3. After the user information is associated with the QR code information, the server generates a disposable token for the mobile phone and stores it to the Redis server, where key is the value of disposable token and value is uuid. An one-time token is returned to the phone as a credential for the "confirm login" request.

In the above code, when the status of the QR code is modified, we wake up the threads blocked in the condition. The purpose of this step is to implement the long polling operation. The design idea of the long polling will be introduced below.

5. Confirm login

When the mobile phone sends the "confirm login" request, the Query parameter carries the uuid and the Header carries the one-time token. After receiving the request, the server first verifies the validity of the one-time token, that is, check whether the uuid corresponding to the one-time token is the same as the uuid in the Query parameters to ensure that the scan code operation and the confirmation operation come from the same mobile end. The verification process can be configured in the interceptor. After the verification is passed, the server invokes the confirmLogin method and sets the status of the QR code to "confirmed":

@ RequestMapping (path = "/ confirm", method = RequestMethod.POST) @ ResponseBodypublic Response confirmLogin (@ RequestParam String uuid) {boolean logged = loginService.confirmLogin (uuid); String msg = logged? "login succeeded!": "QR code is invalid!"; return Response.createResponse (msg, logged);}

The main logic of the confirmLogin method is as follows:

Public boolean confirmLogin (String uuid) {String ticketKey = CommonUtil.buildTicketKey (uuid); LoginTicket loginTicket = (LoginTicket) cacheStore.get (ticketKey); boolean logged = true; Long expired = cacheStore.getExpireForSeconds (ticketKey); if (loginTicket = = null | | expired = = null | | expired = = 0) {logged = false;} else {lock.lock (); try {loginTicket.setStatus (QrCodeStatusEnum.CONFIRMED.getStatus ()); Condition condition = CONDITION_CONTAINER.get (uuid) If (condition! = null) {condition.signal (); CONDITION_CONTAINER.remove (uuid);} cacheStore.put (ticketKey, loginTicket, expired, TimeUnit.SECONDS);} finally {lock.unlock ();}} return logged;}

This method will query whether the QR code has expired according to the uuid, and if not, then modify the status of the QR code.

6. PC polling

The polling operation means that the front end repeatedly sends the same request to the back end to know the change in the data. Polling is divided into long polling and short polling:

Long polling: after the server receives the request, if there is any data, it will return immediately, otherwise the thread will enter the waiting state until the data arrives or times out, and the browser will resend the same request immediately after receiving the response.

Short polling: the server returns immediately after receiving the request, regardless of whether there is any data. After receiving the response, the browser resends the same request after an interval of time.

Because long polling can get real-time response and save more resources than short polling, we consider to use ReentrantLock to implement long polling in the project. The purpose of polling is to see the change in the status of the QR code:

RequestMapping (path = "/ getQrCodeStatus", method = RequestMethod.GET) @ ResponseBodypublic Response getQrCodeStatus (@ RequestParam String uuid, @ RequestParam int currentStatus) throws InterruptedException {JSONObject data = loginService.getQrCodeStatus (uuid, currentStatus); return Response.createResponse (null, data);}

The main logic of the getQrCodeStatus method is as follows:

Public JSONObject getQrCodeStatus (String uuid, int currentStatus) throws InterruptedException {lock.lock (); try {JSONObject data = new JSONObject (); String ticketKey = CommonUtil.buildTicketKey (uuid); LoginTicket loginTicket = (LoginTicket) cacheStore.get (ticketKey); QrCodeStatusEnum statusEnum = loginTicket = = null | | QrCodeStatusEnum.parse (loginTicket.getStatus ()) = QrCodeStatusEnum.INVALID? QrCodeStatusEnum.INVALID: QrCodeStatusEnum.parse (loginTicket.getStatus ()); if (currentStatus = = statusEnum.getStatus ()) {Condition condition = CONDITION_CONTAINER.get (uuid); if (condition = = null) {condition = lock.newCondition (); CONDITION_CONTAINER.put (uuid, condition);} condition.await (LoginConstant.POLL_WAIT_TIME, TimeUnit.SECONDS) } / / after scanning the code, the user returns the profile picture information if (statusEnum = = QrCodeStatusEnum.SCANNED) {User user = userService.getCurrentUser (loginTicket.getUserId ()); data.put ("avatar", user.getAvatar ());} / / generate access_token if (statusEnum = = QrCodeStatusEnum.CONFIRMED) {String accessToken = CommonUtil.generateUUID () for the PC side after confirmation CacheStore.put (CommonUtil.buildAccessTokenKey (accessToken), loginTicket.getUserId (), LoginConstant.ACCESS_TOKEN_EXPIRE_TIME, TimeUnit.SECONDS); data.put ("access_token", accessToken);} data.put ("status", statusEnum.getStatus ()); data.put ("message", statusEnum.getMessage ()); return data;} finally {lock.unlock ();}

The method receives two parameters, namely uuid and currentStatus, in which uuid is used to query the QR code, and currentStatus is used to confirm whether the status of the QR code has changed. If so, it needs to feedback to the PC immediately. We specify that when the PC side polls, the requested parameters need to carry the current status of the QR code.

1. First, the latest status of the QR code is queried according to uuid, and whether it is the same as currentStatus is compared. If the same, the current thread enters a blocking state until it is woken up or times out.

two。 If the status of the QR code is "to be confirmed", the server returns the profile picture information of the scanned user to the PC side (when the QR code is in the "to be confirmed" state, the QR code is bound with the user information, so the user's profile picture can be queried).

3. If the QR code status is confirmed, the server generates a token for the PC side, through which the PC side can identify itself in subsequent requests.

The locking operation in the above code is to enable the thread currently processing the request to enter a blocking state, and when the state of the QR code changes, we wake it up again, so after the scan operation and confirmation login operation is completed, there will also be a process of waking up the thread.

In fact, the locking operation is not designed properly because we only set one lock. Therefore, queries or modifications to different QR codes will preempt the same lock. In theory, the operations of different QR codes should be independent of each other, and even if they are locked, they should be equipped with a lock for each QR code, but the code will be more complex, and there may be other better ways to achieve long polling. Or simply take a short poll. Of course, you can also use WebSocket for persistent connections.

7. Interceptor configuration

Two interceptors are configured in the project, one is used to confirm the identity of the user, that is, to verify whether the token is valid:

@ Componentpublic class LoginInterceptor implements HandlerInterceptor {@ Autowired private HostHolder hostHolder; @ Autowired private CacheStore cacheStore; @ Autowired private UserService userService; @ Override public boolean preHandle (HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {String accessToken = request.getHeader ("access_token"); / / if (StringUtils.isNotEmpty (accessToken)) {String userId = (String) cacheStore.get (CommonUtil.buildAccessTokenKey (accessToken)) exists in access_token User user = userService.getCurrentUser (userId); hostHolder.setUser (user);} return true;} @ Override public void afterCompletion (HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {hostHolder.clear ();}}

If the token is valid, the server obtains the user's information according to the token and stores the user's information in the ThreadLocal container. Requests from both the mobile phone and the PC are handled by the interceptor, such as the "query user information" request on the PC side and the "code scan" request on the mobile side. Because we ignore the device information needed for mobile verification, the same set of verification logic can be used for PC and mobile token.

Another interceptor is used to intercept the "confirm login" request, that is, to verify that the one-time token is valid:

@ Componentpublic class ConfirmInterceptor implements HandlerInterceptor {@ Autowired private CacheStore cacheStore; @ Override public boolean preHandle (HttpServletRequest request, HttpServletResponse response, Object handler) {String onceToken = request.getHeader ("once_token"); if (StringUtils.isEmpty (onceToken)) {return false;} if (StringUtils.isNoneEmpty (onceToken)) {String onceTokenKey = CommonUtil.buildOnceTokenKey (onceToken) String uuidFromCache = (String) cacheStore.get (onceTokenKey); String uuidFromRequest = request.getParameter ("uuid"); if (! StringUtils.equals (uuidFromCache, uuidFromRequest)) {throw new RuntimeException ("illegal one-time token");} / / delete cacheStore.delete (onceTokenKey) after the one-time token check is completed } return true;}}

The interceptor mainly intercepts the "confirm login" request, and it should be noted that it should be deleted immediately after the one-time token authentication is passed.

In the coding process, we simplify many operations, such as: 1. Ignore the device information of the mobile phone; 2. After confirming the login, the PC token is not directly generated for the user, but is generated during polling.

Effect demonstration 1. Tool preparation

Browsers: PC side operation

Postman: imitates the operation of mobile phone.

two。 Data preparation

Because we do not realize the real function of scanning code on the mobile phone, we use Postman to imitate the mobile phone to send the request to the server. First, we need to make sure that the server stores the user's information, that is, execute the following code in the Test class:

@ Testvoid insertUser () {User user = new User (); user.setUserId ("1"); user.setUserName ("John classmate"); user.setAvatar ("/ avatar.jpg"); cacheStore.put ("user:1", user);}

When sending a request, you need to bring the mobile token. Here, we generate a token (mobile token) for users whose useId is "1":

@ Testvoid loginByPhone () {String accessToken = CommonUtil.generateUUID (); System.out.println (accessToken); cacheStore.put (CommonUtil.buildAccessTokenKey (accessToken), "1");}

The mobile token (accessToken) is "aae466837d0246d486f644a3bcfaa9e1" (random value), and then you need to carry this token when you send a "code scan" request.

3. Display of login process by scanning code

Start the project and visit localhost:8080/index:

Click to log in and find the QR code id (uuid) in the developer tool:

Open Postman and send a localhost:8080/login/scan request. The uuid,Header is carried in the Query parameter and the token on the mobile phone is carried:

The above request returns a "scan successful" response, along with an one-time token. At this time, the profile picture of the scanning user is displayed on the PC side:

Send a localhost:8080/login/confirm request in Postman. The Query parameter contains uuid,Header and one-time token:

After the "confirm login" request is sent, the PC side immediately obtains the PC token and successfully queries the user information:

This is the end of the article on "how to scan Code login based on Java". Thank you for reading! I believe that everyone has a certain understanding of the knowledge of "how to achieve code scanning login based on Java". If you want to learn more knowledge, you are welcome to follow the industry information channel.

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report