概述现在使用微信的用户越来越多。如果网站增加微信登录,可以节省大量的用户注册时间,大大减少注册流程。会让用户觉得很方便。下面说一下网页端微信扫码登录的实现方法。准备工作1.实现内网穿透,推荐工具:飞哥,快跳实现内网穿透原因:微信不能访问私网ip地址,而我们在测试的时候,家里ip大多是私网ip地址,所以使用公网ip通过内网穿透映射到我们的服务。2.申请微信公众平台测试账号并配置,点击查看更多Websocket实时通信我们通常有3种方式来实现实时通信:1.Ajax轮询ajax轮询的原理很简单,让浏览器每隔几秒就向服务器发送一个请求,询问服务器是否有新信息。2.http长轮询长轮询机制类似于ajax轮询,都是使用轮询,但是以前是阻塞模型(一直调用,没收如果客户端发起链接后没有消息,永远不会给客户端返回一个response,直到有新的消息才返回,返回后,客户端重新建立连接,周而复始。3.WebSocketHTML5提供的单TCP连接,在WebSocketAPI中,浏览器和服务器只需要进行一次握手,然后在浏览器和服务器之间形成一个快速通道。两者之间可以直接传输数据,无需繁琐的查询和等待。对比:ajax轮询和长轮询是非常耗资源的,而WebSocket只需要经过一次HTTP请求就可以和服务器源源不断的发送和接收消息。实现过程sockjsSockJS是一个浏览器JavaScript库,它提供了一个类网络对象,SockJS提供了一个连贯的、跨浏览器的JavaScriptAPI,它在浏览器和Web服务器之间创建了一个低延迟、全双工、跨域通信通道。SockJS提供浏览器兼容性,首先使用本机WebSocket。如果浏览器不支持WebSocket,SockJS会自动降级为轮询。STOMPSTOMP(SimpleText-OrientatedMessagingProtocol)面向消息的简单文本协议:WebSocket是一种消息架构,不强制使用任何特定的消息协议。它依赖于应用层来解释消息的含义。与HTTP不同,WebSocket在传输层传输数据的实现和处理会将字节流转换为文本/二进制消息。因此,对于实际应用来说,WebSocket的通信形式层次太低了。因此,可以在WebSocket之上使用STOMP协议来为浏览器和服务器服务。为相互通信添加适当的消息语义。STOMP与WebSocket的关系:1.HTTP协议解决了web浏览器的请求和web服务器对请求的响应的细节问题。假设不存在HTTP协议,只能使用TCPsockets来编写web应用程序。双方在应用层的协议三要素可能不一致。2、直接使用WebSocket(SockJS)与使用TCPsockets编写web应用程序非常相似。因为没有高层协议,我们需要定义应用程序之间发送消息的语义,我们也需要保证连接的两端都能遵循这些语义。3.就像HTTP在TCP套接字上添加了一个请求-响应模型层一样,STOMP在WebSocket之上提供了一个基于帧的行格式层来定义消息语义。实际微信扫码登录流程:1.实现前后端WebSocket连接:先在前端安装sockjs-client和stompjs:npminstallsockjs-clientnpminstallstompjsfront-end建立websocket连接简单示例:constsocket=newSockJS('http://localhost:8080/demo-stomp-endpoint');conststompClient=Stomp.over(socket);stompClient.connect({'ws-auth-token':this.uuid},(frame:any)=>{//添加一个uuid用于后续调试,看是不是单例。stompClient.id=uuid();this.stompClientSubject.next(stompClient);});前台注册路由,作为后台主动访问的接口:/***registeredroute*@paramrouterroute*@paramsubject后台回传webSocket时发送数据流*/register(router:string,subject:Subject):void{if(this.observables[router]){thrownewError('Failedtoregisterthekeyrepeatedlyword'+router);}}console.log('注册');这个.stompClient$.pipe(过滤器(v=>v!==null),first()).subscribe(stompClient=>{stompClient.subscribe(this.getUrl(router),(data:any)=>{console.log(data);subject.next(data);});});}背景介绍WebSocket相关依赖:org.springframework.bootspring-boot-starter-websocket背景介绍相关依赖:com.github.binarywangweixin-java-mp4.4.0com.github.binarywangwx-java-mp-spring-boot-starter4.4.0后台定义和前台连接点:/***定义一个连接点(握手第一个webSocket)*/@OverridepublicvoidregisterStompEndpoints(StompEndpointRegistryregistry){registry.addEndpoint("/demo-stomp-endpoint").setAllowedOriginPatterns("http://localhost:4200").withSockJS();}后台定义出口前缀和入口前缀:/***配置消息代理*配置入口前缀和出口前缀*注意:导出需要保留/user前缀。当stomp主动向用户发送数据时,它前面会加上/user前缀(可配置)*/@OverridepublicvoidconfigureMessageBroker(MessageBrokerRegistryconfig){//设置入口前缀并处理所有以appconfig.setApplicationDestinationPrefixes(“/应用程序”);//设置导出前缀处理所有以/stomp开头的导出数据config.enableSimpleBroker("/stomp");}逻辑实现1.首先在打开页面的时候,进行路由注册,让后台主动向前台发起请求//注册前台扫码绑定的路由。扫码绑定后,后台主动发起请求。onScanBindUserQrCode);//注册前端扫码登录的路由。扫码登录后,后台发起请求this.websocketServer.register('/user/stomp/scanLoginQrCode',this.onScanLoginQrCode);然后前台发起获取登录二维码请求,同时传输唯一标识码,用于扫码成功后后台向前台发起成功登录请求:/***获取登录二维码*/getLoginQrCode():Observable{returnthis.httpClient.get(`${this.baseUrl}/getLoginQrCode/${this.websocketServer.uuid}`);}2。后台响应获取登录二维码,同时处理扫描事件:@OverridepublicStringgetLoginQrCode(StringwsLoginToken,HttpSessionhttpSession){try{if(this.logger.isDebugEnabled()){this.logger.info("1.生成回调uuid,请推送给微信。当微信推送带有UUID的二维码时,微信会在用户扫码后发送带有UUID的二维码。信息被推回");}//qrUuid用于换取微信重定向的票据,以便根据票据换取二维码urlStringqrUuid=UUID.randomUUID().toString();wxMpQrCodeTicketwxMpQrCodeTicket=this.weChatMpService.getQrcodeService().qrCodeCreateTmpTicket(qrUuid,10*60);//添加事务处理和进程扫描事件等publicbooleangetExpired(){returnSystem.currentTimeMillis()-beginTime>10*60*1000;}/***扫码后调用此方法*@paramwxMpXmlMessage扫码消息*@paramweChatUser扫码用户*@return输出message*/@OverridepublicWxMpXmlOutMessagehandle(WxMpXmlMessagewxMpXmlMessage,WeChatUserweChatUser){if(this.logger.isDebugEnabled()){this.logger.info("2.用户扫码后触发该方法,当扫码发送成功后,将wsUuid与微信用户绑定,稍后使用wsU");}Stringopenid=wxMpXmlMessage.getFromUser();if(openid==null){this.logger.error("openid为null");}if(weChatUser.getUser()!=null){//这里将随机生成的uuid绑定给用户,返回给前台,再次使用uuid登录Stringuuid=UUID.randomUUID()。toString();System.out.println("uuid是"+uuid);bindWsUuidToWeChatUser(uuid,weChatUser);//这里的wx-auth-token是前端生成的uuid,用于标识唯一的前端simpMessagingTemplate.convertAndSendToUser(wsLoginToken,"/stomp/scanLoginQrCode",uuid);returnnewTextBuilder().build(String.format("登录成功,登录用户为:%s",weChatUser.getUser().getName()),wxMpXmlMessage,null);}别的{//扫码后发现没有绑定,返回一个空的uuid给前台,同时给微信用户返回未绑定的通知//wx-auth-token这里是前台生成的uuid,用于标识前台唯一simpMessagingTemplate.convertAndSendToUser(wx-auth-token,"/stomp/scanLoginQrCode",false);returnnewTextBuilder().build(String.format("登录原理,原因:未绑定微信用户"),wxMpXmlMessage,null);}}});返回this.weChatMpService.getQrcodeService().qrCodePictureUrl(wxMpQrCodeTicket.getTicket());}catch(Exceptione){this.logger.error("获取临时公众号图片时出错:"+e.getMessage());}返回””;}3。前台收到uuid后,使用uuid作为用户名和密码即可正常登录:this.login({username:uuid,password:uuid});4.后台在登录验证方法中增加了基于uuid的验证:/***检查微信扫码登录后的验证ID是否有效*@paramwsAuthUuidwebsocket验证ID*/@OverridepublicbooleancheckWeChatLoginUuidIsValid(StringwsAuthUuid){returnthis.map.containsKey(wsAuthUuid);}实现逻辑图如下:最后是demo的仓库地址,请点击