精华内容
下载资源
问答
  • 扫码登录。之前项目使用的是 ajax轮询的方式。感觉太low了。所以这次用webSocket的方式进行实现好。废话不多说!咱们开始!!一、首先咱们需要一张表这表是干啥的呢?就是记录一下谁扫...

    扫码登录。之前项目使用的是 ajax轮询的方式。感觉太low了。

    所以这次用webSocket的方式进行实现

    好。废话不多说!咱们开始!!

    一、首先咱们需要一张表

    这表是干啥的呢?就是记录一下谁扫码了。谁登录了。

    User_Token表

    字段如下:

    • uuid : 用于确保唯一性

    • userId :谁登录的

    • loginTime :登录时间

    • createTime :创建时间 用于判断是否过期

    • state:是否二维码失效  0有效 1失效

    二、角色都有哪些

    咱们还需要分析一下子。扫码登录这个业务逻辑都有哪些角色

    • android端 or 微信Web端 :扫码

    • PC端 :被扫。登录

    • 服务端:掌控全局,提供接口。

    三、接口都需要哪些?

    有了角色。你用大腿也能想出来接口了对不对!!

    所以咱们的接口有2个!

    • 生成二维码接口:生成一个二维码。二维码中有UUID。

    • 确认身份接口:确定身份以及判断是否二维码过期等

    四、步骤

    那句话怎么说的来着。要把大象装冰箱一共分几步?

    • PC端打开。调用生成二维码接口 并与 服务端建立链接。链接使用uuid进行绑定

    • 微信Web端进行扫码。获取二维码中的uuid。

    • 微信Web端拿到uuid以后。显示是否登录页面。点击确定后 调用 确认身份接口。

    • 确认身份接口通过以后。服务端给PC端发送信息。完成登录。此时链接断开。

    好了!分析完了这些。你们一定在想。。还有完没完啊。。不要在BB了。。赶紧贴代码吧。。

    作者:观众老爷们。我这是在教给你们如何思考的方法呀?

    那么开始贴代码吧!希望大家在看到的同时也可以自己进行思考。

    五、疯狂贴代码

    首先需要获取二维码的代码对不对! 贴!

    //获取登录二维码、放入Token
        @RequestMapping(value = "/getLoginQr" ,method = RequestMethod.GET)
        public void createCodeImg(HttpServletRequest request, HttpServletResponse response){
            response.setHeader("Pragma", "No-cache");
            response.setHeader("Cache-Control", "no-cache");
     
            response.setDateHeader("Expires", 0);
            response.setContentType("image/jpeg");
     
            try {
                //这里没啥操作 就是生成一个UUID插入 数据库的表里
                String uuid = userService.createQrImg();
                response.setHeader("uuid", uuid);
                // 这里是开源工具类 hutool里的QrCodeUtil 
                // 网址:http://hutool.mydoc.io/
                QrCodeUtil.generate(uuid, 300, 300, "jpg",response.getOutputStream());
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    

    有了获取二维码的接口。相对的前端需要调用。

    知识点:动态加载图片流并取出header中的参数

    这里使用了xmlhttp进行处理。

    为什么?

    因为后端返回的是一个流。

    那么流中。就是放置了二维码中的uuid。这个uuid作为一次会话的标识符使用。

    那么前端也需要拿到。跟后端进行webSocket链接。

    这样有人扫码后。服务端才可以使用webSocket的方式通知前端。有人扫码成功了。你做你的业务吧。酱紫。

    所以为了拿到请求中 header中放置的uuid 所以这样通过xmlhttp进行处理

    html
     <div class="qrCodeImg-box" id="qrImgDiv"></div>
    

    js

    $(document).ready(function(){
        initQrImg();
    });
     
     
     function initQrImg(){
                $("#qrImgDiv").empty();
     
                var xmlhttp;
                xmlhttp=new XMLHttpRequest();
                xmlhttp.open("GET",getQrPath,true);
                xmlhttp.responseType = "blob";
                xmlhttp.onload = function(){
                    console.log(this);
                    uuid = this.getResponseHeader("uuid");
     
                    if (this.status == 200) {
                        var blob = this.response;
                        var img = document.createElement("img");
                        img.className = 'qrCodeBox-img';
                        img.onload = function(e) {
                            window.URL.revokeObjectURL(img.src);
                        };
                        img.src = window.URL.createObjectURL(blob);
                        document.getElementById("qrImgDiv").appendChild(img);
     
                        initWebSocket();
                    }
                }
                xmlhttp.send();
            }
     
     
     
           var path = "://localhost:8085";
           var getQrPath =  "http" + path + "/user/getLoginQr";
           var wsPath =     "ws" + path + "/websocket/";
     
     
     
           function initWebSocket(){
     
               if(typeof(WebSocket) == "undefined") {
                   console.log("您的浏览器不支持WebSocket");
               }else{
                   console.log("您的浏览器支持WebSocket");
                   //实现化WebSocket对象,指定要连接的服务器地址与端口  建立连接
                   //等同于socket = new WebSocket("ws://localhost:8083/checkcentersys/websocket/20");
                   var wsPathStr = wsPath+uuid;
                   socket = new WebSocket(wsPathStr);
                   //打开事件
                   socket.onopen = function() {
                       console.log("Socket 已打开");
                       //socket.send("这是来自客户端的消息" + location.href + new Date());
                   };
                   //获得消息事件
                   socket.onmessage = function(msg) {
                       console.log(msg.data);
                       var data = JSON.parse(msg.data);
                       if(data.code == 200){
                           alert("登录成功!");
                           //这里存放自己业务需要的数据。怎么放自己看
                           window.sessionStorage.uuid = uuid;
                           window.sessionStorage.userId = data.userId;
                           window.sessionStorage.projId = data.projId;
     
                           window.location.href = "pages/upload.html"
                       }else{
                           //如果过期了,关闭连接、重置连接、刷新二维码
                           socket.close();
                           initQrImg();
                       }
                       //发现消息进入    开始处理前端触发逻辑
                   };
                   //关闭事件
                   socket.onclose = function() {
                       console.log("Socket已关闭");
                   };
                   //发生了错误事件
                   socket.onerror = function() {
                       alert("Socket发生了错误");
                       //此时可以尝试刷新页面
                   }
               }
     
           }
    

    好了。上面已经提到了前端如何配置webSocket。欢迎关注公众号Java笔记虾,后台回复“后端面试”,送你一份面试题宝典!

    下面说一下

    springBoot中如何操作webSocket

    1、增加pom.xml

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-websocket</artifactId>
    </dependency>
    

    2、增加一个Bean

    /**
     * WebSocket的支持
     * @return
     */
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }
    

    3、定义WebSocketServer

    package com.stylefeng.guns.rest.modular.inve.websocket;
     
    /**
     * Created by jiangjiacheng on 2019/6/4.
     */
    import java.io.IOException;
    import java.util.concurrent.CopyOnWriteArraySet;
     
    import javax.websocket.OnClose;
    import javax.websocket.OnError;
    import javax.websocket.OnMessage;
    import javax.websocket.OnOpen;
    import javax.websocket.Session;
    import javax.websocket.server.PathParam;
    import javax.websocket.server.ServerEndpoint;
    import org.springframework.stereotype.Component;
    import cn.hutool.log.Log;
    import cn.hutool.log.LogFactory;
     
    @ServerEndpoint("/websocket/{sid}")
    @Component
    public class WebSocketServer {
     
        static Log log=LogFactory.get(WebSocketServer.class);
     
        //静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。
        private static int onlineCount = 0;
     
        //concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。
        private static CopyOnWriteArraySet<WebSocketServer> webSocketSet = new CopyOnWriteArraySet<WebSocketServer>();
     
        //与某个客户端的连接会话,需要通过它来给客户端发送数据
        private Session session;
     
        //接收sid
        private String sid="";
     
        /**
         * 连接建立成功调用的方法*/
        @OnOpen
        public void onOpen(Session session,@PathParam("sid") String sid) {
            this.session = session;
            webSocketSet.add(this);     //加入set中
            addOnlineCount();           //在线数加1
            log.info("有新窗口开始监听:"+sid+",当前在线人数为" + getOnlineCount());
            this.sid=sid;
            /*try {
                sendMessage("连接成功");
            } catch (IOException e) {
                log.error("websocket IO异常");
            }*/
        }
     
        /**
         * 连接关闭调用的方法
         */
        @OnClose
        public void onClose() {
            webSocketSet.remove(this);  //从set中删除
            subOnlineCount();           //在线数减1
            log.info("有一连接关闭!当前在线人数为" + getOnlineCount());
        }
     
        /**
         * 收到客户端消息后调用的方法
         *
         * @param message 客户端发送过来的消息*/
        @OnMessage
        public void onMessage(String message, Session session) {
            log.info("收到来自窗口"+sid+"的信息:"+message);
            //群发消息
            for (WebSocketServer item : webSocketSet) {
                try {
                    item.sendMessage(message);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
     
        /**
         *
         * @param session
         * @param error
         */
        @OnError
        public void onError(Session session, Throwable error) {
            log.error("发生错误");
            error.printStackTrace();
        }
        /**
         * 实现服务器主动推送
         */
        public void sendMessage(String message) throws IOException {
            this.session.getBasicRemote().sendText(message);
        }
     
     
        /**
         * 群发自定义消息
         * */
        public static void sendInfo(String message,@PathParam("sid") String sid) throws IOException {
            log.info("推送消息到窗口"+sid+",推送内容:"+message);
            for (WebSocketServer item : webSocketSet) {
                try {
                    //这里可以设定只推送给这个sid的,为null则全部推送
                    if(sid == null) {
                        item.sendMessage(message);
                    }else if(item.sid.equals(sid)){
                        item.sendMessage(message);
                    }
                } catch (IOException e) {
                    continue;
                }
            }
        }
     
        public static synchronized int getOnlineCount() {
            return onlineCount;
        }
     
        public static synchronized void addOnlineCount() {
            WebSocketServer.onlineCount++;
        }
     
        public static synchronized void subOnlineCount() {
            WebSocketServer.onlineCount--;
        }
    }
    

    这样就增加了webSocket的支持啦。

    那么回到刚才的步骤。

    1、首先PC端调用接口展示出来了二维码。

    2、请求二维码中的http请求。就有uuid在 header中。直接取到uuid 作为webSocket的标识sid进行连接。

    3、然后手机端使用相机拿到二维码中的uuid。使用uuid + userid 请求 扫码成功接口。

    贴扫码成功接口

    Controller代码:

      /**
         * 确认身份接口:确定身份以及判断是否二维码过期等
         * @param token
         * @param userId
         * @return
         */
        @RequestMapping(value = "/bindUserIdAndToken" ,method = RequestMethod.GET)
        @ResponseBody
        public Object bindUserIdAndToken(@RequestParam("token") String token ,
                                         @RequestParam("userId") Integer userId,
                                         @RequestParam(required = false,value = "projId") Integer projId){
     
            try {
                return new SuccessTip(userService.bindUserIdAndToken(userId,token,projId));
            } catch (Exception e) {
                e.printStackTrace();
                return new ErrorTip(500,e.getMessage());
            }
     
        }
    

    Service代码

    @Override
        public String bindUserIdAndToken(Integer userId, String token,Integer projId) throws Exception {
     
            QrLoginToken qrLoginToken = new QrLoginToken();
            qrLoginToken.setToken(token);
            qrLoginToken = qrLoginTokenMapper.selectOne(qrLoginToken);
     
            if(null == qrLoginToken){
                throw  new Exception("错误的请求!");
            }
     
            Date createDate = new Date(qrLoginToken.getCreateTime().getTime() + (1000 * 60 * Constant.LOGIN_VALIDATION_TIME));
            Date nowDate = new Date();
            if(nowDate.getTime() > createDate.getTime()){//当前时间大于校验时间
     
                JSONObject jsonObject = new JSONObject();
                jsonObject.put("code",500);
                jsonObject.put("msg","二维码失效!");
                WebSocketServer.sendInfo(jsonObject.toJSONString(),token);
     
                throw  new Exception("二维码失效!");
            }
     
            qrLoginToken.setLoginTime(new Date());
            qrLoginToken.setUserId(userId);
     
            int i = qrLoginTokenMapper.updateById(qrLoginToken);
     
            JSONObject jsonObject = new JSONObject();
            jsonObject.put("code",200);
            jsonObject.put("msg","ok");
            jsonObject.put("userId",userId);
            if(ToolUtil.isNotEmpty(projId)){
                jsonObject.put("projId",projId);
            }
            WebSocketServer.sendInfo(jsonObject.toJSONString(),token);
     
            if(i > 0 ){
                return null;
            }else{
                throw  new Exception("服务器异常!");
            }
        }
    

    逻辑大概就是判断一下 token对不对

    如果对的话。时间是否过期。如果没有过期进行业务逻辑操作

    //这句话比较关键
    WebSocketServer.sendInfo(jsonObject.toJSONString(),token);
    

    就是通知前端 已经登录成功了。并且给他业务所需要的内容。

    然后前端代码接收到了。就进行业务逻辑操作就可以啦。

    往期推荐

    如何利用JVM在线调试工具排查线上问题

    [合集]JVM内存溢出问题的定位方法

    4种方式让你定义的Method在Spring Boot 启动时自动执行

    脚本达人总结的Linux Shell脚本编写经验,看完不踩坑

    这份Redis6.0集群搭建教程,项目肯定用得上

    设计微服务架构,相关设计模式如何运用?

    4种SpringBoot 接口幂等性的实现方案!最后一个80%以上的开发会踩坑

    掌握Git命令的本质,开发时才会得心应手

    Shell 脚本进阶,经典用法及其案例

    Java 线程池配置的常见误区

    优化 Nginx HTTPS 延迟 - 看我如何让Nginx提速 30%的?

    若觉得文章对你有帮助,随手转发分享,也是侠梦继续更新的动力。


    展开全文
  • 后台框架采用SpringMVC,不同的...APP扫描二维码,获取uuid及登录信息,推送给服务端,处理后的登录信息通过websocket返回给PC端,PC端得到登录信息后保存即登录成功。APP扫描确认登录的信息可以采用ActiveMQ进行推...

    后台框架采用SpringMVC,不同的框架可根据逻辑更改即可:

    【思路】- PC端生成二维码,二维码包含uuid(全局唯一标识符),且打通websocket通道,等待服务器返回登录成功信息;APP扫描二维码,获取uuid及登录信息,推送给服务端,处理后的登录信息通过websocket返回给PC端,PC端得到登录信息后保存即登录成功。APP扫描确认登录的信息可以采用ActiveMQ进行推送。

    生成二维码部分引入依赖文件

     

         <dependency>
    	    <groupId>com.google.zxing</groupId>
    	    <artifactId>core</artifactId>
    	    <version>3.1.0</version>
    	</dependency>
    	<dependency>  
             <groupId>com.google.zxing</groupId>  
             <artifactId>javase</artifactId>  
             <version>3.1.0</version>  
         </dependency> 

     

    二维码登录后台控制层Controller

     

    /**
     * 项目名称:dream_user
     * 项目包名:org.fore.user.controller
     * 创建时间:2017年8月8日下午5:29:41
     * 创建者:Administrator-宋发元
     * 创建地点:杭州
     */
    package org.fore.user.controller;
    
    import java.io.IOException;
    import java.io.OutputStream;
    import java.util.HashMap;
    import java.util.Map;
    import java.util.UUID;
    
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.fore.model.user.UserAccount;
    import org.fore.model.user.UserModel;
    import org.fore.user.qrcode.websocket.WebSocketHandler;
    import org.fore.user.service.UserAccountService;
    import org.fore.user.service.UserService;
    import org.fore.utils.jms.JmsSender;
    import org.fore.utils.mvc.TokenUtil;
    import org.fore.utils.mvc.annotation.LimitLess;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.ResponseBody;
    
    import com.alibaba.fastjson.JSONObject;
    import com.google.zxing.BarcodeFormat;
    import com.google.zxing.EncodeHintType;
    import com.google.zxing.MultiFormatWriter;
    import com.google.zxing.WriterException;
    import com.google.zxing.client.j2se.MatrixToImageWriter;
    import com.google.zxing.common.BitMatrix;
    import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
    
    
    /**
     * 描述:控制层
     * @author songfayuan
     * 2017年8月8日下午5:29:41
     */
    @Controller
    @RequestMapping("/qrcodelogin")
    public class QrCodeLoginController {
    
    	private Logger logger = LoggerFactory.getLogger(QrCodeLoginController.class);
    	
    	public static int defaultWidthAndHeight=260;
    	
    	@Autowired
    	private WebSocketHandler webSocketHandler;
    	@Autowired
    	private UserService userService;
    	@Autowired
    	private UserAccountService userAccountService;
    	@Autowired
    	@Qualifier(value = "qrCodeLoginSender")
    	private JmsSender jmsSender;
    	
    	/**
    	 * 描述:PC获取二维码
    	 * @param uuid
    	 * @param request
    	 * @param response
    	 * @throws ServletException
    	 * @throws IOException
    	 * @author songfayuan
    	 * 2017年8月11日上午9:04:43
    	 */
    	@RequestMapping("/getLoginQrCode")
    	@ResponseBody
    	@LimitLess
    	public void getLoginQrCode(String uuid, HttpServletRequest request,
    			HttpServletResponse response) throws ServletException, IOException {
    		//生成参数
    		//String uuid = generateUUID();
    		String host = request.getHeader("Host");
    	    JSONObject data = new JSONObject();
    	    data.put("code", 200);
    	    data.put("msg", "获取二维码成功");
    	    data.put("uuid", uuid);
    	    data.put("host", host);
    	    logger.info("【二维码内容】:{}",data);
            
            //生成二维码
    	    Map<EncodeHintType, Object>  hints=new HashMap<EncodeHintType, Object>();
            // 指定纠错等级  
            hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.L);  
            // 指定编码格式  
            hints.put(EncodeHintType.CHARACTER_SET, "UTF-8");  
            hints.put(EncodeHintType.MARGIN, 1);
            try {
    			BitMatrix bitMatrix = new MultiFormatWriter().encode(data.toString(),BarcodeFormat.QR_CODE, defaultWidthAndHeight, defaultWidthAndHeight, hints);
    			OutputStream out = response.getOutputStream();
    			MatrixToImageWriter.writeToStream(bitMatrix, "png", out);//输出二维码
                out.flush();
                out.close();
                
            } catch (WriterException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		}
    	}
    	
    	/**
    	 * 描述:app确认请求处理
    	 * @param uuid
    	 * @param host
    	 * @param userid
    	 * @author songfayuan
    	 * 2017年8月11日上午9:05:56
    	 */
    	@RequestMapping("/sendCodeLoginInfo")
    	@ResponseBody
    	@LimitLess
    	public void sendCodeLoginInfo(String uuid, String host, Integer userid) {
    		// 注册成功后 或 登录,需要同步账户信息,获取用户基本信息
    		UserAccount account = userAccountService.findCurrentUserAccount(userid);
    		userAccountService.syncAccount(account);
    
    		UserModel userModel = userService.findUserById(userid);
    		userModel = changeUserForShow(userModel);
    		JSONObject token = TokenUtil.generateTokenByQrCodeLogin(userid, host);
    		JSONObject object = new JSONObject();
    		object.put("code", 10086);
    		object.put("uuid", uuid);
    		object.put("userinfo", userModel);
    		object.put("token", token);
    		object.put("msg", "登录成功");
    		//this.webSocketHandler.forwardQrCode(object.toString());
    		jmsSender.sendMessage(object.toString()); //采用ActiveMQ进行推送,也可以直接注入websocket进行发送
    	}
    	//处理用户登录信息
    	private UserModel changeUserForShow(UserModel userModel) {
    		UserModel user = new UserModel();
    		user.setId(userModel.getId());
    		user.setUserName(userModel.getUserName());
    		user.setUserSex(userModel.getUserSex());
    		user.setUserPortrait(userModel.getUserPortrait());
    		return user;
    	}
    	
    	/**
    	 * 描述:唯一标识符
    	 * @return
    	 * @author songfayuan
    	 * 2017年8月11日上午9:06:12
    	 */
    	public static String generateUUID() {
    		String uuid = UUID.randomUUID().toString();
    		uuid = uuid.replace("-", "");
    		Long currentTime = System.currentTimeMillis();
    		String currentDate = String.valueOf(currentTime);
    		return uuid + currentDate;
    	}
    	
    }
    

     

    websocket实现(本案例采用Spring自带的websocket)

     

     

    package org.fore.sms.qrcode.websocket;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.servlet.config.annotation.EnableWebMvc;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
    import org.springframework.web.socket.config.annotation.EnableWebSocket;
    import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
    import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
    import org.springframework.web.socket.server.standard.ServletServerContainerFactoryBean;
    
    @Configuration
    @EnableWebMvc
    @EnableWebSocket
    public class QrCodeLoginWebSocketConfig extends WebMvcConfigurerAdapter implements WebSocketConfigurer {
    
    	@Autowired
    	private QrCodeLoginWebSocketEndPoint endPoint;
    	@Autowired
    	private QrCodeLoginHandshakeInterceptor interceptor;
    
    	@Override
    	public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
    		registry.addHandler(endPoint, "/qrcodelogin.do").addInterceptors(interceptor).setAllowedOrigins("*");
    //		 registry.addHandler(endPoint,
    //		 "/sockjs.do").addInterceptors(interceptor).setAllowedOrigins("*")
    //		 .withSockJS();
    	}
    
    	/**
    	 * Each underlying WebSocket engine exposes configuration properties that
    	 * control runtime characteristics such as the size of message buffer sizes,
    	 * idle timeout, and others.
    	 */
    
    	/**
    	 * For Tomcat, WildFly, and GlassFish add a
    	 * ServletServerContainerFactoryBean to your WebSocket Java config:
    	 */
    	@Bean
    	public ServletServerContainerFactoryBean createWebSocketContainer() {
    		ServletServerContainerFactoryBean container = new ServletServerContainerFactoryBean();
    		container.setMaxTextMessageBufferSize(8192);
    		container.setMaxBinaryMessageBufferSize(8192);
    		return container;
    	}
    
    	/**
    	 * For Jetty, you’ll need to supply a pre-configured Jetty
    	 * WebSocketServerFactory and plug that into Spring’s
    	 * DefaultHandshakeHandler through your WebSocket Java config:
    	 */
    	// @Bean
    	// public DefaultHandshakeHandler handshakeHandler() {
    	//
    	// WebSocketPolicy policy = new WebSocketPolicy(WebSocketBehavior.SERVER);
    	// policy.setInputBufferSize(8192); /* 设置消息缓冲大小 */
    	// policy.setIdleTimeout(600000); /* 10分钟read不到数据的话,则断开该客户端 */
    	//
    	// return new DefaultHandshakeHandler(new JettyRequestUpgradeStrategy(new
    	// WebSocketServerFactory(policy)));
    	// }
    
    }
    

     

    package org.fore.sms.qrcode.websocket;
    
    import java.util.Map;
    
    import javax.servlet.http.HttpServletRequest;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.http.server.ServerHttpRequest;
    import org.springframework.http.server.ServerHttpResponse;
    import org.springframework.stereotype.Component;
    import org.springframework.web.socket.WebSocketHandler;
    import org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor;
    
    @Component
    public class QrCodeLoginHandshakeInterceptor extends HttpSessionHandshakeInterceptor {
    	private Logger logger = LoggerFactory.getLogger(QrCodeLoginHandshakeInterceptor.class);
    
    	@Override
    	public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler,
    			Map<String, Object> attributes) throws Exception {
    		return super.beforeHandshake(request, response, wsHandler, attributes);
    	}
    
    	@Override
    	public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler,
    			Exception ex) {
    		super.afterHandshake(request, response, wsHandler, ex);
    	}
    }
    

     

    package org.fore.sms.qrcode.websocket;
    
    import java.io.IOException;
    import java.util.Map;
    import java.util.UUID;
    import java.util.concurrent.ConcurrentHashMap;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.fore.model.quota.tcp.ReqCode;
    import org.springframework.stereotype.Component;
    import org.springframework.web.socket.CloseStatus;
    import org.springframework.web.socket.TextMessage;
    import org.springframework.web.socket.WebSocketSession;
    import org.springframework.web.socket.handler.TextWebSocketHandler;
    
    import com.alibaba.fastjson.JSON;
    import com.alibaba.fastjson.JSONObject;
    
    @Component
    public class QrCodeLoginWebSocketEndPoint extends TextWebSocketHandler {
    	private Logger logger = LoggerFactory.getLogger(QrCodeLoginWebSocketEndPoint.class);
    
    	private static Map<String, WebSocketSession> sessionMap = new ConcurrentHashMap<>();
    	private static Map<WebSocketSession,String > sessionMap2 = new ConcurrentHashMap<>();
    
    	@Override
    	public void afterConnectionEstablished(WebSocketSession session) throws Exception {
    		logger.info("WebSocketHandler:客户端{}上线", session.getRemoteAddress());
    		String uuid = generateUUID();
    		sessionMap.put(uuid,session);
    		sessionMap2.put(session,uuid);
    	}
    
    	@Override
    	protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
    		String msg = message.getPayload();
    		String ipAddress = session.getRemoteAddress().toString();
    		JSONObject requestData = JSON.parseObject(msg);
    		Integer code = requestData.getInteger("code");
    		JSONObject result = new JSONObject();
    		String uuid	= sessionMap2.get(session);
    		result.put("code", 200);
    		result.put("uuid", uuid);
    		switch (code) {
    		case ReqCode.REQ_QR_CODE:
    			logger.info("WebSocketHandler:客户端{}发送消息{}...", ipAddress, msg);
    			if(session.isOpen())
    			session.sendMessage(new TextMessage(result.toString()));
    			logger.info("WebSocketHandler:客户端{}发送消息{}完成", ipAddress, msg);
    			break;
    		default:
    			break;
    		}
    	}
    
    	@Override
    	public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
    		String ipAddress = session.getRemoteAddress().toString();
    		logger.info("WebSocketHandler:客户端{}下线", ipAddress);
    		logger.info("WebSocketHandler:删除客户端{}的session...", ipAddress);
    		logger.info("WebSocketHandler:删除sessionMap的客户端{}连接...", ipAddress);
    		String uuid = sessionMap2.get(session);
    		sessionMap.remove(uuid);
    		sessionMap2.remove(session);
    		logger.info("WebSocketHandler:删除sessionMap的客户端{}连接完成", ipAddress);
    		logger.info("WebSocketHandler:删除WebSocket客户端{}连接...", ipAddress);
    //		logger.info("{}", sessionMap);
    		sessionMap.remove(session);
    //		logger.info("{}", sessionMap);
    		logger.info("WebSocketHandler:删除WebSocket客户端{}连接完成", ipAddress);
    		logger.info("WebSocketHandler:删除客户端{}的session完成", ipAddress);
    		if(session.isOpen())
    		session.close();
    	}
    
    	@Override
    	public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
    		logger.info("WebSocketHandler:客户端{}异常", session.getRemoteAddress(), exception);
    	}
    	
    	//发送消息
    	public void sendMessage(String userInfo) throws Exception {
    		JSONObject json = JSONObject.parseObject(userInfo);
    		String uuid = json.getString("uuid");
    		WebSocketSession session = sessionMap.get(uuid);
    		if (session == null) {
    			logger.info("app发送给PC的登录信息:{}参数不正确!",userInfo);
    		}else {
    			logger.info("app发送给PC的登录信息:{}",userInfo);
    			session.sendMessage(new TextMessage(userInfo));
    		}
    	}
    	
    	//唯一标识符
    	public static String generateUUID() {
    		String uuid = UUID.randomUUID().toString();
    		uuid = uuid.replace("-", "");
    		Long currentTime = System.currentTimeMillis();
    		String currentDate = String.valueOf(currentTime);
    		return uuid + currentDate;
    	}
    }
    

     

    JMS实现

    package org.fore.sms.qrcode.jms;
    
    import org.fore.utils.jms.Listener;
    import org.fore.sms.qrcode.websocket.QrCodeLoginWebSocketEndPoint;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;
    
    import com.alibaba.fastjson.JSONObject;
    
    @Component
    public class QrCodeLoginListener implements Listener {
    	private Logger logger = LoggerFactory.getLogger(QrCodeLoginListener.class);
    	@Autowired
    	private QrCodeLoginWebSocketEndPoint qrCodeLoginWebSocketEndPoint;
    
    	@Override
    	public void onMessage(String message) {
    		logger.info("app确认登录信息:接收app推送的确定PC登录消息{}", message);
    		JSONObject object = JSONObject.parseObject(message);
    		try {
    			qrCodeLoginWebSocketEndPoint.sendMessage(object.toJSONString());
    		} catch (Exception e) {
    			logger.info("app确认登录信息:接收app推送的确定PC登录消息异常", e);
    		}
    	}
    
    }
    

    核心代码就酱......

     

    转载于:https://www.cnblogs.com/songfayuan/articles/7357880.html

    展开全文
  • 使用微信二维码登录功能,需要有两个前提:一是客户端上需要安装微信 app 。 二是用户需要登录到到微信 app 。 https://wx.qq.com/   JAVA Websocket消息推送   为什么要有这两个条件呢?那是因为...
    最近在做一个扫码登录功能,为此我还在网上搜了一下关于微信的扫描登录的实现方式。当这个功能完成了后,我决定将整个实现思路整理出来,方便自己以后查看也方便其他有类似需求的程序猿些。
    要实现扫码登录我们需要解决两个问题:
    1.  在没有输入用户名及密码的情况下,如何解决权限安全问题?换句话讲,如何让服务器知道扫码二维码的客户端是一个合法的用户?
    2.  服务器根据用户在客户端的选择如何实时在网页上作出相应的响应?

    首先我们先理一下微信的实现思路,来方便我们理解解决这一难题的思路方向。微信登录的二维码实际上是将一个URL转换成二维码的形式,而通过微信客户端扫码后,无非就是打开了这个url, 我捕捉到的微信二维码的urlhttps://login.weixin.qq.com/l/YdmTu30I5A== ,这个url里的YdmTu30I5A==代表的是本次会话的唯一ID, 这个有点儿类似浏览器里的session id,通过这个ID,微信就能定向将确认结果反馈到网页上。使用微信二维码登录功能,需要有两个前提:一是客户端上需要安装微信app 二是用户需要登录到到微信apphttps://wx.qq.com/

     

    JAVA Websocket消息推送

     

    为什么要有这两个条件呢?那是因为微信在确认是否允许登录到网页版的时候,微信需要提取当前app的登录信息并将上面的session ID一并发给服务器,这样服务器收到了登录信息和sessionID后就可以确认两件事:一是用来确认登录的客户端的用户是验证过的;二是通过session ID服务器知道将反馈结果推送到哪个网页。

       所以针对第一点,我们的关键在于,在扫描前要确保用户是已经被验证过且合法的用户(验证方式可以是用户名+密码,也可以是一个secure key),在选择是否登录时将这个结果一并推送到服务器端,就好了。如果用户没有验证是否合法,可以像微信的处理方式一样直接告诉用户二维码不可识别或提示请先登录到app

      有了身份验证,那么现在就解决第二个问题,如何将反馈结果实时地显示在网页上呢?有朋友可能会说,客户端这边很简单发一个请求到后台就好了,而网页上用ajax定时发送到服务器端看是否有反馈。我不赞成这种做法,因为ajax轮询方式十分消耗客户端和服务器端资源!这里涉及到另一个技术-web实时推送技术,使用推送技术可以节约服务器端和客户端的资源,可以稳定地推送和接收任何消息。我在实现的过程中我采用了第三方推送服务-GoEasy推送,用它是实现非常简单,我们项目里的其他功能也用到了GoEasy web实时推送服务,所以在此我直接就用的GoEasy推送来将登录反馈结果推送到服务器。我的实现步骤非常简单,将传送的session ID作为客户端与网页端的通信channel,网页端订阅用session ID作为值得channel,客户端将验证结果和session ID发送到服务器端,服务器端可以通过这个channel主动将结果推送给网页版!如果客户端也需要做相应的反馈的话,那么客户端也只需要订阅这个channel,然后服务器端会同时将结果推送给网页版和客户端,收到消息后,就可以根据需求在goeasy的回调函数里做你想做的事情了。关于goeasy推送的使用,大家可以参考这篇博客: http://www.cnblogs.com/jishaochengduo/articles/5552645.html 另外GoEasy推送官网上也有一个demoGoEasy二维码扫码登录demo,大家可以去看看效果.

      希望对大家有帮助,如有理解错误的地方,还请大家斧正。

    转载于:https://my.oschina.net/miaomiaogong1/blog/800066

    展开全文
  • 前端生成二维码,存储随机ID,并注册到websocket的服务端,手机扫码后,带上token访问校验端,并且授权给核心工厂,确认登陆后,发送登录成功的信息和授权以后的token给前端,前端做token存储和页面跳转 附件: ...

    ##原理:
    前端生成二维码,存储随机ID,并注册到websocket的服务端,手机扫码后,带上token访问校验端,并且授权给核心工厂,确认登陆后,发送登录成功的信息和授权以后的token给前端,前端做token存储和页面跳转

    附件:

    文件名地址
    后端压缩包scanLogin.zip
    前端demo页面websocket.html

    ## 核心代码展示

    1、websocket配置(启动器)

    @SpringBootApplication
    @EnableWebSocket
    public class LoginDemoApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(LoginDemoApplication.class, args);
        }
        @Bean
        public ServerEndpointExporter serverEndpointExporter() {
            return new ServerEndpointExporter();
        }
    }
    

    2、websocket服务端

    
    import com.saoma.logindemo.factory.CodeFactory;
    import org.springframework.stereotype.Component;
    
    import javax.websocket.OnOpen;
    import javax.websocket.Session;
    import javax.websocket.server.PathParam;
    import javax.websocket.server.ServerEndpoint;
    import java.io.IOException;
    import java.util.Iterator;
    import java.util.Map;
    import java.util.concurrent.ConcurrentHashMap;
    
    /**
     * @Classname WebSocketDemo
     * @Description
     * @Date 2021/2/209:52
     * @Author zjhchester
     */
    @ServerEndpoint("/scanlogin/{code}")
    @Component
    public class WebSocketServer {
        private static Map<String, WebSocketServer> clients = new ConcurrentHashMap();
        private Session session;
        private String code;
        private static Integer threadStatus = 0;
        public WebSocketServer() {
    
        }
        private CodeFactory codeFactory = SpringUtil.getBean(CodeFactory.class);
        @OnOpen
        public void onOpen(@PathParam("code") String code, Session session) throws IOException {
            this.code = code;
            this.session = session;
            clients.put(code, this);
    
            System.out.println(code);
            codeFactory.setCode(code,session.toString());
            sendMessageTo("connect success!",code);
        }
        public void sendMessageTo(String message, String to) throws IOException {
            Iterator var3 = clients.values().iterator();
            while(var3.hasNext()) {
                WebSocketServer item = (WebSocketServer)var3.next();
                if (item.code.equals(to)) {
                    item.session.getAsyncRemote().sendText(message);
                }
            }
        }
    }
    

    3、核心工厂

    import org.springframework.stereotype.Component;
    
    import java.util.concurrent.ConcurrentHashMap;
    
    /**
     * @Classname CodeFacotory
     * @Description 扫码登录核心工厂
     * @Date 2021/2/209:56
     * @Author zjhchester
     */
    @Component
    public  class CodeFactory {
        /**
         * key: 生成的随机码
         * value: 绑定的socket  sessionID
         */
        private static ConcurrentHashMap<String,String> codeFactory;
        static {
            codeFactory = new ConcurrentHashMap<>(1);
        }
        public String getCode(String code){
            return codeFactory.get(code);
        }
        public String setCode(String code,String session){
            return codeFactory.put(code,session);
        }
    }
    

    4、校验controller

    import com.saoma.logindemo.conf.SpringUtil;
    import com.saoma.logindemo.conf.WebSocketServer;
    import com.saoma.logindemo.factory.CodeFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import java.io.IOException;
    import java.util.Objects;
    import java.util.UUID;
    
    /**
     * @Classname scanDemo
     * @Description
     * @Date 2021/2/209:50
     * @Author zjhchester
     */
    @RestController
    public class ScanDemoController {
        private final static String TRUE= "true";
        private final CodeFactory codeFactory;
        private final WebSocketServer webSocketServer;
        public ScanDemoController(CodeFactory codeFactory, WebSocketServer webSocketServer) {
            this.codeFactory = codeFactory;
            this.webSocketServer = webSocketServer;
        }
    
        /**
         * 校验登陆
         * @return result
         */
        @GetMapping("/codeverify")
        public String codeCreate(String code) throws IOException {
            String session = codeFactory.getCode(code);
            if(Objects.nonNull(session)){
                System.out.println(session);
                codeFactory.setCode(code,"true");
                webSocketServer.sendMessageTo("已在手机上扫描,请确认登录",code);
            }
            return code;
        }
    
    
        @GetMapping("/confirmlogin")
        public Boolean confirmLogin(String code) throws IOException {
            String verifyResult = codeFactory.getCode(code);
            if(TRUE.equals(verifyResult)){
                SpringUtil.getBean(WebSocketServer.class).sendMessageTo("登录成功",code);
                return true;
            }else{
                return false;
            }
        }
    }
    
    展开全文
  • 最近在做一个扫码登录功能,为此我还在网上搜了一下关于微信的扫描登录的实现方式。当这个功能完成了后,...换句话讲,如何让服务器知道扫码二维码的客户端是一个合法的用户?2.服务器根据用户在客户端的选择如何实...
  • 首先使用的是websocket进行消息的传递,当我们在pc端请求到二维码图片后,打开微信扫一扫,扫描二维码后,就可以在websocket里接收到扫描消息,然后进行自己的业务逻辑处理,具体代码如下: websocketLink方法是在...
  • 基于WebSocket实现二维码的被扫描即失效(VUE+SpringBoot) 算是记录贴,目的是给小程序的webview使用的,因为微信的限制,目前还没全部完成,在浏览器已经可以实现了 下面正文: 后端代码: 1、依赖 <...
  • 作者:93年颈椎病人blog.csdn.net/q826qq1878/article/details/91041679最近单位又有一个新Java项目。涉及到扫码登录。之前项目使用的是 aj...
  • tomcat(apache-tomcat-7.0.64),Java API for WebSocket(JSR 356),ssh(struts2-2.3.36,spring-3.2.7.RELEASE, hibernate-3.6.10.Final) 1:Tomcat从7.0.27开始支持WebSocket,从7.0.47开始支持JSR-356。刚开始本想...
  • 最近在做一个扫码登录功能,为此我还在网上搜了一下关于微信的扫描登录的实现方式。当这个功能完成了后,...换句话讲,如何让服务器知道扫码二维码的客户端是一个合法的用户?2.服务器根据用户在客户端的选择如何实...
  • 最近在做一个扫码登录功能,为此我还在网上搜了一下关于微信的扫描登录的实现方式。...换句话讲,如何让服务器知道扫码二维码的客户端是一个合法的用户? 2. 服务器根据用户在客户端的选择如何实时在网页...
  • 最近在做一个扫码登录功能,为此我还在网上搜了一下关于微信的扫描登录的实现方式。...换句话讲,如何让服务器知道扫码二维码的客户端是一个合法的用户? 2. 服务器根据用户在客户端的选择如何实时在网页上...
  • 基于 springBoot 实现webSocket方式的扫码登录

    千次阅读 多人点赞 2019-06-06 14:25:15
    所以这次用webSocket的方式进行实现 好。废话不多说!咱们开始!! 一、首先咱们需要一张表 这表是干啥的呢? 就是记录一下谁扫码了。谁登录了。 User_Token表 字段如下: 1、uuid : 用于确保唯一性 2、...
  • 最近在做一个扫码登录功能,为此我还在网上搜了一下关于微信的扫描登录的实现方式。当这个功能完成了后,...换句话讲,如何让服务器知道扫码二维码的客户端是一个合法的用户?2.服务器根据用户在客户端的选择如何实...
  • CSDN上的大部分我都下过了不能用,此SpringBoot 集成WebSocket ,亲测可用 支持jdk1.8,
  • 最近在做一个扫码登录功能,为此我还在网上搜了一下关于微信的扫描登录的实现方式。...换句话讲,如何让服务器知道扫码二维码的客户端是一个合法的用户? 2. 服务器根据用户在客户端的选择如何实时在网页...
  • 因为使用laravel, 所以使用artisan编写服务,也可以使用其他文件编写, 直接用php 命令执行, 参照博客PHP使用workerman实现后端消息推送前端, 完成微信登录建议先读一下上边这边博客, 有助于读者理解整个推送过程代码...
  • User_Token表 字段如下: uuid : 用于确保唯一性 userId :谁登录的 loginTime :登录时间 createTime :创建时间 用于判断是否过期 state:是否二维码失效 0有效 1失效 二、角色都有哪些 咱们还需要分析一下子。...
  • java二维码登录实现

    2021-04-19 21:57:53
    二维码登录原理 让服务端知道是那个用户要登录,验证通过后 服务端通过webscoket 告知 前端 登录成功即可 前端二维码登录实现 链接: 仿知乎pc登录注册二维码登录页面. 下载下来修改一下即可使用 java后端代码 ...
  • 二维码登录demo

    2013-08-28 18:35:01
    包含两个文件:一个是显示二维码,请求服务端推送登录token的demo,主要实现websocket请求,有源码。 另外一个是模拟手机扫码的demo,因为只是一个网络请求,就不传源码了。有需要的朋友可以发站内信找我索要。 ...
  • #Java实现二维码登录

    千次阅读 2020-05-30 13:34:53
    二维码登录的流程: 1)用户选择二维码登录; 前端发送获取二维码请求,服务器收到请求后生成一个uuid(用于绑定二维码),然后根据指定网址和uuid生成对应的二维码,将uuid作为key,一个对应的code(生成二维码...
  • 在pc端获取后端给的token值来生产二维码(这里后端给了我两个url:一个是二维码url,一个是ws的 url),用微信扫描二维码跳转到小程序写的确定登录页面(进行确定);然后在PC端使用WebSocket来达到获取用户信息来...
  • 最近在做一个扫码登录功能,为此我还在网上搜了一下关于微信的扫描登录的实现方式。当这个功能完成了后,我决定将整个实现思路整理出来,方便自己以后查看也方便其他有类似需求的程序猿些。 要实现扫码登录我们需要...
  • Websocket 实现扫码二维码登录首先简单的讲一下二维码实现登录的步骤: 1、网页端向服务端请求二维码,服务端生成一个二维码提供给网页端; 这个二维码其实是一个地址 2、客户端通过扫描网页端的二维码,跳转到...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 7,643
精华内容 3,057
关键字:

websocket二维码登录