• 欢迎访问web前端中文站,JavaScript,CSS3,HTML5,web前端demo
  • 如果您觉得本站非常有看点,那么赶紧使用Ctrl+D 收藏web前端中文站吧

websocket实现APP扫描二维码,自动登陆网站(扫码登录)

JAVA web前端中文站 2年前 (2017-11-12) 2606次浏览 已收录 0个评论

我了解网站扫描二维码并实现自动登陆功能是从微信哪里看到的,该功能体验还不错,减少密码被盗的风险。那么一个普通的网站该如何实现此功能呢?本文将借助websocket+java 实现这一功能。

更多精彩内容请看 web 前端中文站
http://www.lisa33xiaoq.net 可按 Ctrl + D 进行收藏

实现思路

  1. 后台系统,提供生成带参数的二维码的接口,这个参数就是唯一值(场景值)
  2. 访问到登录网站页面 时,生成二维码
  3. 用户拿 APP 扫码后,APP 直接将场景值中的唯一值和用户信息反馈给后台系统
  4. 后台系统将用户访问的唯一值信息记录到 redis,key 就是唯一值(场景值)
  5. 网站端做轮训去查询 redis 中是否有这个唯一值的数据,如果有就获取 APP 用户信息信息,没有就五秒一次轮训,登录后就不在做轮训(从二维码弹出之后开始做轮训,关闭二维码后停止轮训)
  6. 这里的唯一值是可以自己定义的,我用的是截取了几位的时间戳

实现步骤

根据上面的实现思路,我们用 java 写一个获取带参数的临时二维码接口。以下是主要代码:

 // 临时二维码  private final static String QR_SCENE = "QR_SCENE";   
 // 永久二维码   private final static String QR_LIMIT_SCENE = "QR_LIMIT_SCENE";   
 // 永久二维码(字符串)   private final static String QR_LIMIT_STR_SCENE = "QR_LIMIT_STR_SCENE";    
 // 创建二维码   private String create_ticket_path = "https://api.weixin.qq.com/cgi-bin/qrcode/create";   
 // 通过 ticket 换取二维码   private String showqrcode_path = "https://mp.weixin.qq.com/cgi-bin/showqrcode";   
 @RequestMapping("getQrcode") public @ResponseBody String getQrcode(
 @RequestParam(value = "sceneId")int sceneId) throws Exception{  
 String ticket = createTempTicket(tokenService.getToken(),"2592000",sceneId);  
 LOGGER.info("get wechat qrcode  ==> start");  
 LOGGER.info("sceneId :"+sceneId);  
 LOGGER.info("ticket :"+ticket);  
 LOGGER.info("get wechat qrcode  ==> end");  return ticket; } 
 /**   * 创建临时带参数二维码   * @param accessToken   */ 
 /*@expireSeconds 该二维码有效时间,以秒为单位。 最大不超过 2592000(即 30 天),此字段如果不填,*/
 /*则默认有效期为 30 秒。* @param sceneId 场景 Id   * @return // web 前端中文站:www.lisa33xiaoq.net */   
 public String createTempTicket(String accessToken, String expireSeconds, int sceneId) {    
 TreeMap<String,String> params = new TreeMap<String,String>();    
 params.put("access_token", accessToken);    
 Map<String,Integer> intMap = new HashMap<String,Integer>();  
 intMap.put("scene_id",sceneId);    
 Map<String,Map<String,Integer>> mapMap = new HashMap<String,Map<String,Integer>>();    
 mapMap.put("scene", intMap);    
 Map<String,Object> paramsMap = new HashMap<String,Object>();    
 paramsMap.put("expire_seconds", expireSeconds);    
 paramsMap.put("action_name", QR_SCENE);    
 paramsMap.put("action_info", mapMap);    
 String data = new Gson().toJson(paramsMap);    
 String tse = HttpRequestUtil.HttpsDefaultExecute(HttpRequestUtil.POST_METHOD,create_ticket_path,params,data);    
 JSONObject jsonObject = JSONObject.fromObject(tse);  LOGGER.info("ticket :"+jsonObject.getString("ticket"));   
 return showqrcode_path+"?ticket="+jsonObject.getString("ticket"); }

二维码的生成,相信大家都会,我这里直接调用微信的接口是为了整合微信做其他功能。

配合 SpringMVC 做一个接口:

 @RequestMapping("getQrcode") 
 public @ResponseBody Hashtable getQrcode(int sceneId){  
 System.out.println(sceneId);  
 Hashtable param = new Hashtable();  
 param.put("sceneId", sceneId);  
 String qrcodePath = HttpUtil.postRequest(Constant.getValue("get_qrcode"), param);  
 System.out.println(" qrcodePath ==> "+qrcodePath);  
 param.put("path", qrcodePath);  return param;
 // web 前端中文站:www.lisa33xiaoq.net }

网站端:登陆页面中做轮训,每隔几秒查询一次 redis,如果有用户信息就登陆。

 var timestamp = new Date().getTime() + ""; 
 var str = timestamp.substring(8, timestamp.length);  
 window.setInterval(function() {  getUser(cont); }, 10000);  
 function getUser() {  $.ajax({   type : 'get',   data : {    
 sceneId : str   },   dataType : 'json',   url : "getUser.do",   success : function(data) {    
 if (data.msg == "success") {     location.reload();     }   },   error : function(data) {    
 if (data.msg == "success") {     location.reload();    }   }
 // web 前端中文站:www.lisa33xiaoq.net  }); }

上面使用的轮询效率不高,我们可以借助websocket来实现(实际应用中自己判断浏览器是否支持 WebSocket,支持就优先 WebSocket,不支持就是用轮询)。

使用websocket建立一个连接很容易,代码如下:

 var timestamp = new Date().getTime() + ""; 
 timestamp = timestamp.substring(0, timestamp.length-3);    
 var websocket = null; //判断当前浏览器是否支持 WebSocket if ('WebSocket' in window) {  
 websocket = new WebSocket("ws://www.lisa33xiaoq.net/wx/websocket/"+timestamp); } else {  
 alert('当前浏览器  Not support websocket'); } 
 //连接发生错误的回调方法 
 websocket.onerror = function () {  
 console.log("WebSocket 连接发生错误"); }; 
 //连接成功建立的回调方法 websocket.onopen = function () {  
 console.log("WebSocket 连接成功"); } 
 //接收到消息的回调方法 
 websocket.onmessage = function (event) {  
 jQuery("#username").html(event.data);  
 jQuery("#singup").css("display", "none");  
 jQuery("#user").show();  
 jQuery("#singout").show();  
 layer.closeAll(); } 
 //连接关闭的回调方法 
 websocket.onclose = function () {  
 console.log("WebSocket 连接关闭"); } 
 //监听窗口关闭事件,当窗口关闭时,主动去关闭 websocket 连接, 
 //防止连接还没断开就关闭窗口,server 端会抛异常。 
 window.onbeforeunload = function () {  
 closeWebSocket(); } 
 //关闭 WebSocket 连接 
 function closeWebSocket() {  
 websocket.close(); }

服务器端 socket 的建立,在建立 socket 链接的时候,将每个页面传过来的唯一标识{sceneId}与会话信息 session 进行绑定,可以使用 map 实现,这里用的 ConcurrentMap。

 /**  * @ServerEndpoint 注解是一个类层次的注解,*/   
 /* 它的功能主要是将目前的类定义成一个 websocket 服务器端,  */ 
 /*注解的值将被用于监听用户连接的终端访问 URL 地址,  */ 
 /*客户端可以通过这个 URL 来连接到 WebSocket 服务器端  */ 
 @Component @ServerEndpoint("/websocket/{sceneId}") 
 public class WebSocketController { 
 //静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。 
 private static int onlineCount = 0; 
 //concurrent 包的线程安全 Set,用来存放每个客户端对应的 MyWebSocket 对象。 
 //若要实现服务端与单一客户端通信的话,可以使用 Map 来存放,其中 Key 可以为用户标识 
 private static ConcurrentMap<String,WebSocketController>  
 webSocketMap   = new ConcurrentHashMap<String,WebSocketController>(); 
 //与某个客户端的连接会话,需要通过它来给客户端发送数据 private Session session; 
 /**  * 连接建立成功调用的方法  * @param session  可选的参数。session 为与某个客户端的连接会话,  */ 
 /*需要通过它来给客户端发送数据  */ 
 @OnOpen public void onOpen(@PathParam("sceneId") String sceneId,Session session){  
 this.session = session;  
 webSocketMap.put(sceneId, this);   
 //在线数加 1  System.out.println("唯一 key 为:" + sceneId); } 
 /**  * 连接关闭调用的方法  */ 
 @OnClose public void onClose(@PathParam("sceneId") String sceneId){  
 webSocketMap.remove(sceneId);
 //从 map 中删除 } 
 /**  * 收到客户端消息后调用的方法  */ 
 /*@param message 客户端发送过来的消息  * @param session 可选的参数  */ 
 @OnMessage public void onMessage(String message,Session session) {  
 System.out.println("来自客户端的消息:" + message);  
 JSONObject jsonobject = JSONObject.fromObject(message);  
 Hashtable params= (Hashtable)JSONObject.toBean(jsonobject,Hashtable.class);  
 //群发消息  
 WebSocketController webSocketController = webSocketMap.get(params.get("equipmentType"));  
 try {   webSocketController.sendMessage((String)params.get("nickname"));  
 } catch (IOException e) {   e.printStackTrace();  } } 
 /**  * 发生错误时调用  * @param session  * @param error  */ 
 @OnError public void onError(Session session, Throwable error){  
 System.out.println("发生错误");  error.printStackTrace(); } 
 /**  * 这个方法与上面几个方法不一样。没有用注解,是根据自己需要添加的方法。  */
 @param message  * @throws IOException 
 public void sendMessage(String message) throws IOException{  
 this.session.getBasicRemote().sendText(message); }

以上就是整个扫码登录功能的核心代码。有兴趣的可以结合我的实现思路和部分代码自己实现一个。

【注:本文源自网络文章资源,由站长整理发布】


web 前端中文站 , 版权所有丨如未注明 , 均为原创丨本网站采用BY-NC-SA协议进行授权
转载请注明原文链接:websocket 实现 APP 扫描二维码,自动登陆网站(扫码登录)
喜欢 (1)
发表我的评论
取消评论
表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址