网站开发的可行性,wordpress信息,作品提示优化要删吗,网址大全名称前言 本打算用CountDownLatch来实现#xff0c;但有个问题我没有考虑#xff0c;就是当用户APP没有扫二维码的时候#xff0c;线程会阻塞5分钟#xff0c;这反而造成性能的下降。好吧#xff0c;现在回归传统方式#xff1a;前端ajax每隔1秒或2秒发一次请求#xff0c;去…前言 本打算用CountDownLatch来实现但有个问题我没有考虑就是当用户APP没有扫二维码的时候线程会阻塞5分钟这反而造成性能的下降。好吧现在回归传统方式前端ajax每隔1秒或2秒发一次请求去查询后端的登录状态。 一、支付宝和微信的实现方式 1.支付宝的实现方式 每隔1秒会发起一次http请求调用https://securitycore.alipay.com/barcode/barcodeProcessStatus.json?securityIdweb%7Cauthcenter_qrcode_login%7C【UUID】_callbacklight.request._callbacks.callback3 如果获取到认证信息则跳转进入内部系统。 如图所示 2.微信的实现方式 每隔1分钟调用一次 https://login.wx.qq.com/cgi-bin/mmwebwx-bin/login?loginicontrueuuid【UUID】tip0r-1524754438_1521943100181 而请求一次的时间预计是1分钟如果没有查到到认证信息则会返回 window.code408;没有扫码就会一直等待。当一定时间不扫码二维码页面就会强制刷新。 我猜想后端的机制和我上篇《spring boot高性能实现二维码扫码登录(上)——单服务器版》类似。 那么如果用户长时间不扫二维码服务器的线程将不会被唤醒微信是怎么做到高性能的。如果有园友知道可以给我留言。 3.我的实现方式 好了我这里选用支付宝的实现方式。因为简单粗暴还高效。 流程如下 1.前端发起成二维码的请求并得到登录UUID 2.后端生成UUID后写入Redis。 3.前端每隔1秒发起一次请求从Redis中获取认证信息如果没有认证信息则返回waiting状态如果查询到认证信息则将认证信息写入seesion。 二、代码编写 pom.xml引入Redis及Session的依赖 !-- redis --dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-data-redis/artifactId/dependency!-- session --dependencygroupIdorg.springframework.session/groupIdartifactIdspring-session-data-redis/artifactId/dependency完整的pom.xml ?xml version1.0 encodingUTF-8?
project xmlnshttp://maven.apache.org/POM/4.0.0 xmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexsi:schemaLocationhttp://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsdmodelVersion4.0.0/modelVersiongroupIdcom.demo/groupIdartifactIdauth/artifactIdversion0.0.1-SNAPSHOT/versionpackagingjar/packagingnameauth/namedescription二维码登录/descriptionparentgroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-parent/artifactIdversion2.0.0.RELEASE/versionrelativePath / !-- lookup parent from repository --/parentpropertiesproject.build.sourceEncodingUTF-8/project.build.sourceEncodingproject.reporting.outputEncodingUTF-8/project.reporting.outputEncodingjava.version1.8/java.version/propertiesdependenciesdependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-thymeleaf/artifactId/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-web/artifactId/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-devtools/artifactIdscoperuntime/scope/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-test/artifactIdscopetest/scope/dependency!-- zxing --dependencygroupIdcom.google.zxing/groupIdartifactIdcore/artifactIdversion3.3.0/version/dependencydependencygroupIdcom.google.zxing/groupIdartifactIdjavase/artifactIdversion3.3.0/version/dependency!-- redis --dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-data-redis/artifactId/dependency!-- session --dependencygroupIdorg.springframework.session/groupIdartifactIdspring-session-data-redis/artifactId/dependencydependencygroupIdcommons-codec/groupIdartifactIdcommons-codec/artifactId/dependency/dependenciesbuildpluginsplugingroupIdorg.springframework.boot/groupIdartifactIdspring-boot-maven-plugin/artifactId/plugin/plugins/build/project pom.xml App.java入口类 package com.demo.auth;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;SpringBootApplication
public class App {public static void main(String[] args) {SpringApplication.run(App.class, args);}} App.java resources/application.properties 中配置使用redis存储session # session
spring.session.store-typeredis 前端页面index.html和login.html !DOCTYPE html
html xmlns:thhttp://www.thymeleaf.org
head
meta charsetUTF-8
title二维码登录/title
/head
bodyh1二维码登录/h1h4a target_blank hrefhttp://www.cnblogs.com/GoodHelper/from刘冬的博客/a/h4h3 th:text登录用户 ${user}/h3br /a href/logout注销/a
/body
/html index.html !DOCTYPE html
html xmlns:thhttp://www.thymeleaf.org
head
meta charsetUTF-8
title二维码登录/title
script src//cdn.bootcss.com/angular.js/1.5.6/angular.min.js/script
script typetext/javascript/*![CDATA[*/var app angular.module(app, []);app.controller(MainController, function($rootScope, $scope, $http) {//二维码图片src$scope.src null;//获取二维码$scope.getQrCode function() {$http.get(/login/getQrCode).success(function(data) {if (!data || !data.loginId || !data.image)return;$scope.src data:image/png;base64, data.image$scope.getResponse(data.loginId)});}//获取登录响应$scope.getResponse function(loginId) {$http.get(/login/getResponse/ loginId).success(function(data) {if (!data) {setTimeout($scope.getQrCode(), 1000);return;}//一秒后重新获取登录二维码if (!data.success) {if (data.stats waiting) {//一秒后再次调用setTimeout(function() {$scope.getResponse(loginId);}, 1000);} else {//重新获取二维码setTimeout(function() {$scope.getQrCode(loginId);}, 1000);}return;}//登录成功进去首页location.href /}).error(function(data, status) {//一秒后重新获取登录二维码setTimeout(function() {$scope.getQrCode(loginId);}, 1000);})}$scope.getQrCode();});/*]]*/
/script
/head
body ng-appapp ng-controllerMainControllerh1扫码登录/h1h4a target_blank hrefhttp://www.cnblogs.com/GoodHelper/from刘冬的博客/a/h4img ng-showsrc ng-src{{src}} /
/body
/html bean配置类BeanConfig.java package com.demo.auth;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.StringRedisTemplate;Configuration
public class BeanConfig {Beanpublic StringRedisTemplate template(RedisConnectionFactory connectionFactory) {return new StringRedisTemplate(connectionFactory);}} 登录处理类 /*** 登录配置 博客出处http://www.cnblogs.com/GoodHelper/**/
Configuration
public class WebSecurityConfig implements WebMvcConfigurer {/*** 登录session key*/public final static String SESSION_KEY user;Beanpublic SecurityInterceptor getSecurityInterceptor() {return new SecurityInterceptor();}public void addInterceptors(InterceptorRegistry registry) {InterceptorRegistration addInterceptor registry.addInterceptor(getSecurityInterceptor());// 排除配置addInterceptor.excludePathPatterns(/error);addInterceptor.excludePathPatterns(/login);addInterceptor.excludePathPatterns(/login/**);// 拦截配置addInterceptor.addPathPatterns(/**);}private class SecurityInterceptor extends HandlerInterceptorAdapter {Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)throws Exception {HttpSession session request.getSession();if (session.getAttribute(SESSION_KEY) ! null)return true;// 跳转登录String url /login;response.sendRedirect(url);return false;}}
} WebSecurityConfig MainController类修改为 package com.demo.auth;import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;import javax.imageio.ImageIO;
import javax.servlet.http.HttpSession;import org.apache.commons.codec.binary.Base64;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.SessionAttribute;import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;/*** 控制器* * author 刘冬博客http://www.cnblogs.com/GoodHelper**/
Controller
public class MainController {private static final String LOGIN_KEY key.value.login.;Autowiredprivate StringRedisTemplate redisTemplate;GetMapping({ /, index })public String index(Model model, SessionAttribute(WebSecurityConfig.SESSION_KEY) String user) {model.addAttribute(user, user);return index;}GetMapping(login)public String login() {return login;}/*** 获取二维码* * return*/GetMapping(login/getQrCode)public ResponseBody MapString, Object getQrCode() throws Exception {MapString, Object result new HashMap();String loginId UUID.randomUUID().toString();result.put(loginId, loginId);// app端登录地址String loginUrl http://localhost:8080/login/setUser/loginId/;result.put(loginUrl, loginUrl);result.put(image, createQrCode(loginUrl));ValueOperationsString, String opsForValue redisTemplate.opsForValue();opsForValue.set(LOGIN_KEY loginId, loginId, 5, TimeUnit.MINUTES);return result;}/*** app二维码登录地址这里为了测试才传{user},实际项目中user是通过其他方式传值* * param loginId* param user* return*/GetMapping(login/setUser/{loginId}/{user})public ResponseBody MapString, Object setUser(PathVariable String loginId, PathVariable String user) {ValueOperationsString, String opsForValue redisTemplate.opsForValue();String value opsForValue.get(LOGIN_KEY loginId);if (value ! null) {opsForValue.set(LOGIN_KEY loginId, user, 1, TimeUnit.MINUTES);}MapString, Object result new HashMap();result.put(loginId, loginId);result.put(user, user);return result;}/*** 等待二维码扫码结果的长连接* * param loginId* param session* return*/GetMapping(login/getResponse/{loginId})public ResponseBody MapString, Object getResponse(PathVariable String loginId, HttpSession session) {MapString, Object result new HashMap();result.put(loginId, loginId);ValueOperationsString, String opsForValue redisTemplate.opsForValue();String user opsForValue.get(LOGIN_KEY loginId);// 长时间不扫码二维码失效。需重新获二维码if (user null) {result.put(success, false);result.put(stats, refresh);return result;}// 登录扫码二维码if (user.equals(loginId)) {result.put(success, false);result.put(stats, waiting);return result;}// 登录成,认证信息写入sessionsession.setAttribute(WebSecurityConfig.SESSION_KEY, user);result.put(success, true);result.put(stats, ok);return result;}/*** 生成base64二维码* * param content* return* throws Exception*/private String createQrCode(String content) throws Exception {try (ByteArrayOutputStream out new ByteArrayOutputStream()) {HashtableEncodeHintType, Object hints new HashtableEncodeHintType, Object();hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);hints.put(EncodeHintType.CHARACTER_SET, utf-8);hints.put(EncodeHintType.MARGIN, 1);BitMatrix bitMatrix new MultiFormatWriter().encode(content, BarcodeFormat.QR_CODE, 400, 400, hints);int width bitMatrix.getWidth();int height bitMatrix.getHeight();BufferedImage image new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);for (int x 0; x width; x) {for (int y 0; y height; y) {image.setRGB(x, y, bitMatrix.get(x, y) ? 0xFF000000 : 0xFFFFFFFF);}}ImageIO.write(image, JPG, out);return Base64.encodeBase64String(out.toByteArray());}}GetMapping(/logout)public String logout(HttpSession session) {// 移除sessionsession.removeAttribute(WebSecurityConfig.SESSION_KEY);return redirect:/login;}
} 三、运行效果 如图所示效果与上篇一样。 目前我在考虑微信的方式。我打算采用 CountDownLatch await一分钟然后使用消息订阅广播唤醒线程的方式来实现此功能。如果有懂原理的朋友可以给我留言。 代码下载 如果你觉得我的博客对你有帮助可以给我点儿打赏左侧微信右侧支付宝。 有可能就是你的一点打赏会让我的博客写的更好:) 返回玩转spring boot系列目录 作者刘冬.NET 博客地址http://www.cnblogs.com/GoodHelper/ 欢迎转载但须保留版权 转载于:https://www.cnblogs.com/GoodHelper/p/8643071.html