扫码登录

这个帖子网上很多了,但是都是讲理论知识,我呢,喜欢搞代码。既然搞完了,就贴出来备忘一下,也可以分享一下。

重复理论步骤:

1、进入网站-生成UUID

2、跳转到二维码页面(二维码包含UUID)

3、二维码页面写一个js,自动请求服务器查询二维码是否被扫

4、服务器收到请求,查询,如果还没被扫,进入等待,先不返回结果

5、一旦被扫,立即返回结果,页面js收到响应,做后续处理

OK,步骤是这样的没错,不过有一点缺点,步骤3中如果请求超时怎么办。

这个微信web登录有示例,服务器被请求后,持续等待25秒左右,然后结束请求,js端重新发起请求,就这样25秒为周期,不停发起长链接请求。

看下微信web的长连接

不说了,贴代码了,我这里使用的是spring-boot ,spring版本是4.3.6

1、生成UUID


    @RequestMapping("/")  
    String index(HttpServletRequest request,HttpServletResponse response)  
    {  
        System.out.println("进入首页,先生成UUID");  

        request.setAttribute("uuid", UUID.randomUUID());  

        return "pages/index";  
    }  

2、生成二维码,页面部分

  <body>  
        <div class="main">  
            <div class="title">  
                <img id="qrcode" alt="" src="">  
            </div>  
            <div id="result" class="title"></div>  
        </div>  

    </body>  

页面js:


    $(function() {  
            // 文档就绪  
            $("#qrcode").attr("src", "/qrcode/${uuid}");  
            $("#result").html("使用手机扫描二维码");  
            keepPool();//一加载就进入自动请求-见步骤3  
        });  

3、页面js自动请求服务器查询是否被扫

    function keepPool(){  
            $.post("/pool", {  
                uuid : "${uuid}",  
            }, function(data) {  
                if(data=='success'){  
                  $("#result").html("登录成功");  
                }else if(data=='timeout'){  
                    $("#result").html("登录超时,请刷新重试");  
                }else{  
                    keepPool();  
                }  
            });  
        }  

4、服务器收到请求,这里服务器端的事情还是蛮多的,分解一下

  1、首先要生成二位码,对应 $("#qrcode").attr("src", "/qrcode/${uuid}");

  2、生成二位码后,需要将uuid放入到缓存,我是将UUID作为建,新建一个对象作为值(这里可以采用redis),我为了学习方便,自己写了个缓存

  3、查询是否被扫,对应$.post("/pool", { uuid : "${uuid}"}......,这时候有一个等待的功能(缓存中的对象来控制,这个对象的键就是UUID)

  4、被扫后,立马通知等待者(这里是通过缓存中的对象来通知消息的)

  5、上面说了好多次对象了,对的,都是同一个,接着贴代码了

4.1-4.2 生成二位码,我这里使用的google的zxing

  @RequestMapping("/qrcode/{uuid}")  
        @ResponseBody  
        String createQRCode(@PathVariable String uuid,HttpServletResponse response)  
        {  
            System.out.println("生成二维码");  

            String text = "http://172.20.16.194:8080/login/"+uuid;  
            int width = 300;     
            int height = 300;     
            String format = "png";     
            //将UUID放入缓存  
            ScanPool pool = new ScanPool();  
            PoolCache.cacheMap.put(uuid, pool);  
            try  
            {  
                Map<EncodeHintType, Object> hints= new HashMap<EncodeHintType, Object>();     
                hints.put(EncodeHintType.CHARACTER_SET, "utf-8");  
                //hints.put(EncodeHintType.MARGIN, 1);  
                hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H); //容错率  
                BitMatrix bitMatrix = new MultiFormatWriter().encode(text, BarcodeFormat.QR_CODE, width, height,hints);  
                MatrixToImageWriter.writeToStream(bitMatrix, format, response.getOutputStream());  
            } catch (WriterException e)  
            {  
                // TODO Auto-generated catch block  
                e.printStackTrace();  
            } catch (IOException e)  
            {  
                // TODO Auto-generated catch block  
                e.printStackTrace();  
            }  
            return null;  
        }  

看到对象ScanPool没有,这就是那个对象,PoolCache是那个缓存,既然说了,先贴这两个类。

ScanPool.java

    public class ScanPool  
    {  

        //创建时间  
        private Long createTime = System.currentTimeMillis();  

        //登录状态  
        private boolean scanFlag = false;  

        public boolean isScan(){  
            return scanFlag;  
        }  

        public void setScan(boolean scanFlag){  
            this.scanFlag = scanFlag;  
        }  

        /** 
         * 获取扫描状态,如果还没有扫描,则等待固定秒数 
         * @param wiatSecond 需要等待的秒数 
         * @return 
         */  
        public synchronized boolean getScanStatus(){  
            try  
            {  
                if(!isScan()){ //如果还未扫描,则等待  
                    this.wait();  
                }  
                if (isScan())  
                {  
                    return true;  
                }  
            } catch (InterruptedException e)  
            {  
                // TODO Auto-generated catch block  
                e.printStackTrace();  
            }  
            return false;  
        }  

        /** 
         * 扫码之后设置扫码状态 
         */  
        public synchronized void scanSuccess(){  
            try  
            {  
                setScan(true);  
                this.notifyAll();  
            } catch (Exception e)  
            {  
                // TODO Auto-generated catch block  
                e.printStackTrace();  
            }  
        }  

        public synchronized void notifyPool(){  
            try  
            {  
                this.notifyAll();  
            } catch (Exception e)  
            {  
                // TODO Auto-generated catch block  
                e.printStackTrace();  
            }  
        }  

        public Long getCreateTime()  
        {  
            return createTime;  
        }  

        public void setCreateTime(Long createTime)  
        {  
            this.createTime = createTime;  
        }  

    }  

PoolCache.java


    public class PoolCache  
    {  
        //缓存超时时间 10分钟  
        private static Long timeOutSecond = 600L;  

        //每半小时清理一次缓存  
        private static Long cleanIntervalSecond = 1800L;  

        public static Map<String, ScanPool> cacheMap = new HashMap<String, ScanPool>();  

        static{  
            new Thread(new Runnable()  
            {  

                @Override  
                public void run()  
                {  
                    // TODO Auto-generated method stub  
                    while (true)  
                    {  
                        try  
                        {  
                            Thread.sleep(cleanIntervalSecond*1000);  
                        } catch (InterruptedException e)  
                        {  
                            // TODO Auto-generated catch block  
                            e.printStackTrace();  
                        }  
                        clean();  
                    }  
                }  

                public void clean(){  
                    if(cacheMap.keySet().size() > 0){  
                        Iterator<String> iterator = cacheMap.keySet().iterator();  
                        while (iterator.hasNext())  
                        {  
                            String key = iterator.next();  
                            ScanPool pool = cacheMap.get(key);  
                            if(System.currentTimeMillis() - pool.getCreateTime() > timeOutSecond * 1000){  
                                cacheMap.remove(key);  
                            }  
                        }  
                    }  
                }  
            }).start();  
        }  

    }  

4.3.查询是否被扫

    @RequestMapping("/pool")  
        @ResponseBody  
        String pool(String uuid){  
            System.out.println("检测["+uuid+"]是否登录");  

            ScanPool pool = PoolCache.cacheMap.get(uuid);  

            if(pool == null){  
                return "timeout";  
            }  

            //使用计时器,固定时间后不再等待扫描结果--防止页面访问超时  
            new Thread(new ScanCounter(uuid)).start();  

            boolean scanFlag = pool.getScanStatus();  
            if(scanFlag){  
                return "success";  
            }else{  
                return "fail";  
            }  
        }  

这里看到,有一个防止页面请求超时的,是写了一个计时器,达到固定时长就停掉,返回一个fail,这里我就不贴了,有需要的可以下载我源码看

4.4.被扫后



    @RequestMapping("/login/{uuid}")  
        @ResponseBody  
        String login(@PathVariable String uuid){  

            ScanPool pool = PoolCache.cacheMap.get(uuid);  

            if(pool == null){  
                return "timeout,scan fail";  
            }  

            pool.scanSuccess();  

            return "scan success";  
        }  

ok,结束

对了,附上地址,可以直接运行。项目下下来放入ide,直接run App.java

git:https://code.csdn.net/xiasihua88/spring-boot.git

download:http://download.csdn.net/detail/xiasihua88/9779057

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值