掃二維碼自動(dòng)跳轉(zhuǎn)【java】詳解
這個(gè)帖子網(wǎng)上很多了,但是都是講理論知識(shí),我呢,喜歡搞代碼。既然搞完了,就貼出來(lái)備忘一下,也可以分享一下。
重復(fù)理論步驟:
1、進(jìn)入網(wǎng)站-生成UUID
2、跳轉(zhuǎn)到二維碼頁(yè)面(二維碼包含UUID)
3、二維碼頁(yè)面寫一個(gè)js,自動(dòng)請(qǐng)求服務(wù)器查詢二維碼是否被掃
4、服務(wù)器收到請(qǐng)求,查詢,如果還沒被掃,進(jìn)入等待,先不返回結(jié)果
5、一旦被掃,立即返回結(jié)果,頁(yè)面js收到響應(yīng),做后續(xù)處理
OK,步驟是這樣的沒錯(cuò),不過(guò)有一點(diǎn)缺點(diǎn),步驟3中如果請(qǐng)求超時(shí)怎么辦。
這個(gè)微信web登錄有示例,服務(wù)器被請(qǐng)求后,持續(xù)等待25秒左右,然后結(jié)束請(qǐng)求,js端重新發(fā)起請(qǐng)求,就這樣25秒為周期,不停發(fā)起長(zhǎng)鏈接請(qǐng)求。
看下微信web的長(zhǎng)連接
不說(shuō)了,貼代碼了,我這里使用的是spring-boot ,spring版本是4.3.6
1、生成UUID
@RequestMapping("/") String index(HttpServletRequest request,HttpServletResponse response) { System.out.println("進(jìn)入首頁(yè),先生成UUID"); request.setAttribute("uuid", UUID.randomUUID()); return "pages/index"; }
2、生成二維碼,頁(yè)面部分
<body> <div class="main"> <div class="title"> <img id="qrcode" alt="" src=""> </div> <div id="result" class="title"></div> </div> </body>
頁(yè)面js:
$(function() { // 文檔就緒 $("#qrcode").attr("src", "/qrcode/${uuid}"); $("#result").html("使用手機(jī)掃描二維碼"); keepPool();//一加載就進(jìn)入自動(dòng)請(qǐng)求-見步驟3 });
3、頁(yè)面js自動(dòng)請(qǐng)求服務(wù)器查詢是否被掃
function keepPool(){ $.post("/pool", { uuid : "${uuid}", }, function(data) { if(data=='success'){ $("#result").html("登錄成功"); }else if(data=='timeout'){ $("#result").html("登錄超時(shí),請(qǐng)刷新重試"); }else{ keepPool(); } }); }
4、服務(wù)器收到請(qǐng)求,這里服務(wù)器端的事情還是蠻多的,分解一下
1、首先要生成二位碼,對(duì)應(yīng) $("#qrcode").attr("src", "/qrcode/${uuid}");
2、生成二位碼后,需要將uuid放入到緩存,我是將UUID作為建,新建一個(gè)對(duì)象作為值(這里可以采用redis),我為了學(xué)習(xí)方便,自己寫了個(gè)緩存
3、查詢是否被掃,對(duì)應(yīng)$.post("/pool", { uuid : "${uuid}"}......,這時(shí)候有一個(gè)等待的功能(緩存中的對(duì)象來(lái)控制,這個(gè)對(duì)象的鍵就是UUID)
4、被掃后,立馬通知等待者(這里是通過(guò)緩存中的對(duì)象來(lái)通知消息的)
5、上面說(shuō)了好多次對(duì)象了,對(duì)的,都是同一個(gè),接著貼代碼了
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); //容錯(cuò)率 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; }
看到對(duì)象ScanPool沒有,這就是那個(gè)對(duì)象,PoolCache是那個(gè)緩存,既然說(shuō)了,先貼這兩個(gè)類。
ScanPool.java
public class ScanPool { //創(chuàng)建時(shí)間 private Long createTime = System.currentTimeMillis(); //登錄狀態(tài) private boolean scanFlag = false; public boolean isScan(){ return scanFlag; } public void setScan(boolean scanFlag){ this.scanFlag = scanFlag; } /** * 獲取掃描狀態(tài),如果還沒有掃描,則等待固定秒數(shù) * @param wiatSecond 需要等待的秒數(shù) * @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; } /** * 掃碼之后設(shè)置掃碼狀態(tài) */ 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 { //緩存超時(shí)時(shí)間 10分鐘 private static Long timeOutSecond = 600L; //每半小時(shí)清理一次緩存 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("檢測(cè)["+uuid+"]是否登錄"); ScanPool pool = PoolCache.cacheMap.get(uuid); if(pool == null){ return "timeout"; } //使用計(jì)時(shí)器,固定時(shí)間后不再等待掃描結(jié)果--防止頁(yè)面訪問(wèn)超時(shí) new Thread(new ScanCounter(uuid)).start(); boolean scanFlag = pool.getScanStatus(); if(scanFlag){ return "success"; }else{ return "fail"; } }
這里看到,有一個(gè)防止頁(yè)面請(qǐng)求超時(shí)的,是寫了一個(gè)計(jì)時(shí)器,達(dá)到固定時(shí)長(zhǎng)就停掉,返回一個(gè)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,結(jié)束
源碼下載地址:http://xz.jb51.net:81/201905/yuanma/springboot(jb51.net).rar
以上所述是小編給大家介紹的java掃二維碼自動(dòng)跳轉(zhuǎn)詳解整合,希望對(duì)大家有所幫助,如果大家有任何疑問(wèn)請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)腳本之家網(wǎng)站的支持!
- java后臺(tái)實(shí)現(xiàn)js關(guān)閉本頁(yè)面,父頁(yè)面指定跳轉(zhuǎn)或刷新操作
- Java 在PDF中添加頁(yè)面跳轉(zhuǎn)按鈕功能(代碼演示)
- Java Web實(shí)現(xiàn)session過(guò)期后自動(dòng)跳轉(zhuǎn)到登陸頁(yè)功能【基于過(guò)濾器】
- java實(shí)現(xiàn)301跳轉(zhuǎn)和重定向的方法
- java servlet 幾種頁(yè)面跳轉(zhuǎn)的方法
- java Struts2 在攔截器里的跳轉(zhuǎn)問(wèn)題
- Java界面編程實(shí)現(xiàn)界面跳轉(zhuǎn)
相關(guān)文章
SpringBoot注解篇之@Resource與@Autowired的使用區(qū)別
@Resource 注解和 @Autowired 注解都是在 Spring Framework 中進(jìn)行依賴注入的注解,那么你知道他們有什么區(qū)別嗎,本文就來(lái)介紹一下2023-12-12Java synchronized重量級(jí)鎖實(shí)現(xiàn)過(guò)程淺析
這篇文章主要介紹了Java synchronized重量級(jí)鎖實(shí)現(xiàn)過(guò)程,synchronized是Java里的一個(gè)關(guān)鍵字,起到的一個(gè)效果是"監(jiān)視器鎖",它的功能就是保證操作的原子性,同時(shí)禁止指令重排序和保證內(nèi)存的可見性2023-02-02如何自定義feign調(diào)用實(shí)現(xiàn)hystrix超時(shí)、異常熔斷
這篇文章主要介紹了自定義feign調(diào)用實(shí)現(xiàn)hystrix超時(shí)、異常熔斷的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-06-06Spring的@RequestParam對(duì)象綁定方式
這篇文章主要介紹了Spring的@RequestParam對(duì)象綁定方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-10-10SpringBoot基本web開發(fā)demo過(guò)程解析
這篇文章主要介紹了SpringBoot基本web開發(fā)demo過(guò)程解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-11-11Springboot RestTemplate設(shè)置超時(shí)時(shí)間的方法(Spring boot
這篇文章主要介紹了Springboot RestTemplate設(shè)置超時(shí)時(shí)間的方法,包括Spring boot 版本<=1.3和Spring boot 版本>=1.4,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),感興趣的朋友跟隨小編一起看看吧2024-08-08一篇超詳細(xì)的Spring Boot對(duì)jdbc支持的文章
JdbcTemplate 是在JDBC API基礎(chǔ)上提供了更抽象的封裝,并提供了基于方法注解的事務(wù)管理能力。 通過(guò)使用SpringBoot自動(dòng)配置功能并代替我們自動(dòng)配置beans,下面給大家介紹spring boot中使用JdbcTemplate相關(guān)知識(shí),一起看看吧2021-07-07