preface
This article will introduce the general idea of logging in to Demo by scanning code based on SpringBoot + Vue + Android. The complete code has been uploaded to GitHub. Web side experience address: http://47.116.72.33/ (only one month validity), APK download address: github.com/zhangjiwei1… . User name: not empty, password: 123456, the effect is at the end of the article, if there is any problem with the overall implementation, welcome to exchange and discuss, the realization of part of the two-dimensional code scan code login is what principle.
Project introduction
Back end: SpringBoot, Redis.
Front-end: Vue, Vue Router, VueX, Axios, VUE-QR, ElemntUI.
Android: ZXing, XUI, YHttp.
Implementation approach
The overall sweep login and OAuth2.0 authentication logic are similar as follows:
When the user selects code sweep for login, it can be regarded as A: the front end sends authorization request and waits for the APP to scan code.
The scanning of the app by the user can be regarded as B: scanning for authorization and returning a temporary Token for secondary authentication.
User login confirmation in APP can be regarded as C: login confirmation, and authorized user login in Web.
Step D is when the backend returns an official Token after the user confirms login.
Subsequently, the front-end accesses the background interface based on the formal Token, and the formal operations on the Web end can be regarded as E and F.
Reasons for secondary authentication
The reason after users scan code also need to confirm the login again rather than just log in reason, it is for user safety consideration, avoid the user swept others need to log in to the qr code, without confirmation will directly logged in, may lead to others in situations where we don’t know to visit our information.
Implementation steps
-
When a user accesses the web page, he or she selects scan code to log in
The user will send a QR code generation request to the back end when scanning for login. The back end generates a UUID and saves it to Redis (fixed validity time). The STATUS is UNUSED. And set a timer, every once in a while according to the UUID in the content of the TWO-DIMENSIONAL code, send a request to the back end, obtain the status of the two-dimensional code, update the content displayed on the interface.
Generate the back-end interface of two-dimensional code:
/** * Generate the content of the QR code **@returnResults the * / @GetMapping("/generate") public BaseResult generate(a) { String code = IdUtil.simpleUUID(); redisCache.setCacheObject(code, CodeUtils.getUnusedCodeInfo(), DEFAULT_QR_EXPIRE_SECONDS, TimeUnit.SECONDS); return BaseResult.success(GENERATE_SUCCESS, code); } Copy the code
The front-end obtains the content and generates the TWO-DIMENSIONAL code:
getToken() { this.codeStatus = 'EMPTY' this.tip = 'Obtaining login code, please wait.' // Valid time is 60 seconds this.effectiveSeconds = 60 clearInterval(this.timer) request({ method: 'get'.url: '/code/generate' }).then((response) = > { // The request is successful. Set the content of the QR code and update the relevant information this.code = `${HOST}/code/scan? code=${response.data}` this.codeStatus = 'UNUSED' this.tip = 'Please use mobile phone scan to log in' this.timer = setInterval(this.getTokenInfo, 2000) }).catch(() = > { this.getToken() }) } Copy the code
Back-end interface for returning status information of two-dimensional code:
/** * Obtain the status information of the QR code **@paramCode:@returnResults the * / @GetMapping("/info") public BaseResult info(String code) { CodeVO codeVO = redisCache.getCacheObject(code); if (codeVO == null) { return BaseResult.success(INVALID_CODE, StringUtils.EMPTY); } return BaseResult.success(GET_SUCCESS, codeVO); } Copy the code
Front-end polling to obtain two-dimensional code status:
getTokenInfo() { this.effectiveSeconds-- // The qr code has expired if (this.effectiveSeconds <= 0) { this.codeStatus = 'EXPIRE' this.tip = 'Qr code has expired, please refresh' return } // Query the status of the QR code request({ method: 'get'.url: '/code/info'.params: { code: this.code.substr(this.code.indexOf('=') + 1) } }).then(response= > { const codeVO = response.data // The qr code has expired if(! codeVO || ! codeVO.codeStatus) {this.codeStatus = 'EXPIRE' this.tip = 'Qr code has expired, please refresh' return } // The status of the QR code is login if (codeVO.codeStatus === 'CONFIRMING') { this.username = codeVO.username this.avatar = codeVO.avatar this.codeStatus = 'CONFIRMING' this.tip = 'Scan code successfully, please confirm on your mobile phone' return } // The status of the QR code is confirm login if (codeVO.codeStatus === 'CONFIRMED') { clearInterval(this.timer) const token = codeVO.token store.commit('setToken', token) this.$router.push('/home') Message.success('Login successful') return}})}Copy the code
-
Scanning code with mobile phone changes the status of the TWO-DIMENSIONAL code
When a user uses a mobile phone code scan (the user has logged in to the correct APP; otherwise, the scan will jump to the customized promotional page), it will update the status of the TWO-DIMENSIONAL code to CONFIRMING (to be confirmed), and add the user name and profile picture information to the Redis cache for front-end display. In addition, it will return the user’s login information (login address, browser, operating system) to the APP for display, and generate a temporary Token to the APP (fixed validity period).
Background processing when users scan codes:
/** * Process the qr code in the unused state **@paramCode:@param token token * @returnResults the * / private BaseResult handleUnusedQr(String code, String token) { // Verify the token passed through app access boolean isLegal = JwtUtils.verify(token); if(! isLegal) {return BaseResult.error(AUTHENTICATION_FAILED); } // Save the user name and profile picture for display String username = JwtUtils.getUsername(token); CodeVO codeVO = CodeUtils.getConfirmingCodeInfo(username, DEFAULT_AVATAR_URL); redisCache.setCacheObject(code, codeVO, DEFAULT_QR_EXPIRE_SECONDS, TimeUnit.SECONDS); // Return the login address, browser, operating system, and a temporary token to app String address = HttpUtils.getRealAddressByIp(); String browser = HttpUtils.getBrowserName(); String os = HttpUtils.getOsName(); String tmpToken = JwtUtils.sign(username); // Store the user name content in Redis with the temporary token as the key redisCache.setCacheObject(tmpToken, username, DEFAULT_TEMP_TOKEN_EXPIRE_MINUTES, TimeUnit.MINUTES); LoginInfoVO loginInfoVO = new LoginInfoVO(address, browser, os, tmpToken); return BaseResult.success(SCAN_SUCCESS, loginInfoVO); } Copy the code
-
Confirm login by mobile phone
When the user clicks “confirm login” in the APP, he/she will send a request to update the status with the generated temporary Token. The status of the TWO-DIMENSIONAL code will be updated to “CONFIRMED login”. At the same time, the back-end will generate a formal Token and store it in Redis. Then use this Token to log in.
The backend processing confirms the login code:
/** * Process the qr code with unconfirmed status **@paramCode:@param token token * @returnResults the * / private BaseResult handleConfirmingQr(String code, String token) { // Get the user name using the temporary token and delete the temporary token from redis String username = redisCache.getCacheObject(token); if (StringUtils.isBlank(username)) { return BaseResult.error(AUTHENTICATION_FAILED); } redisCache.deleteObject(token); // Generate a formal token based on the user name and save it in Redis for use by the front end String formalToken = JwtUtils.sign(username); CodeVO codeVO = CodeUtils.getConfirmedCodeInfo(username, DEFAULT_AVATAR_URL, formalToken); redisCache.setCacheObject(code, codeVO, DEFAULT_QR_EXPIRE_SECONDS, TimeUnit.SECONDS); return BaseResult.success(CONFIRM_SUCCESS); } Copy the code