扫二维码自动跳转【java】

来源:互联网 发布:阿里云怎么禁止ip访问 编辑:程序博客网 时间:2024/04/30 05:09


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


重复理论步骤:

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}")@ResponseBodyString 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 blocke.printStackTrace();} catch (IOException e){// TODO Auto-generated catch blocke.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 blocke.printStackTrace();}return false;}/** * 扫码之后设置扫码状态 */public synchronized void scanSuccess(){try{setScan(true);this.notifyAll();} catch (Exception e){// TODO Auto-generated catch blocke.printStackTrace();}}public synchronized void notifyPool(){try{this.notifyAll();} catch (Exception e){// TODO Auto-generated catch blocke.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(){@Overridepublic void run(){// TODO Auto-generated method stubwhile (true){try{Thread.sleep(cleanIntervalSecond*1000);} catch (InterruptedException e){// TODO Auto-generated catch blocke.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")@ResponseBodyString 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}")@ResponseBodyString 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


       



0 0