掃二維碼自動跳轉(zhuǎn)【java】詳解
這個帖子網(wǎng)上很多了,但是都是講理論知識,我呢,喜歡搞代碼。既然搞完了,就貼出來備忘一下,也可以分享一下。
重復(fù)理論步驟:
1、進(jìn)入網(wǎng)站-生成UUID
2、跳轉(zhuǎn)到二維碼頁面(二維碼包含UUID)
3、二維碼頁面寫一個js,自動請求服務(wù)器查詢二維碼是否被掃
4、服務(wù)器收到請求,查詢,如果還沒被掃,進(jìn)入等待,先不返回結(jié)果
5、一旦被掃,立即返回結(jié)果,頁面js收到響應(yīng),做后續(xù)處理
OK,步驟是這樣的沒錯,不過有一點(diǎn)缺點(diǎn),步驟3中如果請求超時怎么辦。
這個微信web登錄有示例,服務(wù)器被請求后,持續(xù)等待25秒左右,然后結(jié)束請求,js端重新發(fā)起請求,就這樣25秒為周期,不停發(fā)起長鏈接請求。
看下微信web的長連接


不說了,貼代碼了,我這里使用的是spring-boot ,spring版本是4.3.6
1、生成UUID
@RequestMapping("/")
String index(HttpServletRequest request,HttpServletResponse response)
{
System.out.println("進(jìn)入首頁,先生成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("使用手機(jī)掃描二維碼");
keepPool();//一加載就進(jìn)入自動請求-見步驟3
});
3、頁面js自動請求服務(wù)器查詢是否被掃
function keepPool(){
$.post("/pool", {
uuid : "${uuid}",
}, function(data) {
if(data=='success'){
$("#result").html("登錄成功");
}else if(data=='timeout'){
$("#result").html("登錄超時,請刷新重試");
}else{
keepPool();
}
});
}
4、服務(wù)器收到請求,這里服務(wù)器端的事情還是蠻多的,分解一下
1、首先要生成二位碼,對應(yīng) $("#qrcode").attr("src", "/qrcode/${uuid}");
2、生成二位碼后,需要將uuid放入到緩存,我是將UUID作為建,新建一個對象作為值(這里可以采用redis),我為了學(xué)習(xí)方便,自己寫了個緩存
3、查詢是否被掃,對應(yīng)$.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
{
//創(chuàng)建時間
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
{
//緩存超時時間 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";
}
//使用計時器,固定時間后不再等待掃描結(jié)果--防止頁面訪問超時
new Thread(new ScanCounter(uuid)).start();
boolean scanFlag = pool.getScanStatus();
if(scanFlag){
return "success";
}else{
return "fail";
}
}
這里看到,有一個防止頁面請求超時的,是寫了一個計時器,達(dá)到固定時長就停掉,返回一個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掃二維碼自動跳轉(zhuǎn)詳解整合,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復(fù)大家的。在此也非常感謝大家對腳本之家網(wǎng)站的支持!
相關(guān)文章
maven 在執(zhí)行package,install,deploy時使用clean與不使用clean的不同之處
有時候用mvn install后,新改的內(nèi)容不生效,一定要后來使用mvn clean install 才生效,由于之前沒有做記錄,以及記不清是什么情況下才會出現(xiàn)的問題,于是想看看clean和不clean的區(qū)別,感興趣的朋友跟隨小編一起看看吧2021-08-08
Java序列化和反序列化_動力節(jié)點(diǎn)Java學(xué)院整理
把對象轉(zhuǎn)換為字節(jié)序列的過程稱為對象的序列化,把字節(jié)序列恢復(fù)為對象的過程稱為對象的反序列化。接下來通過本文給大家介紹Java序列化和反序列化及主要的兩種用途,感興趣的的友參考下吧2017-05-05
Java如何使用JWT實現(xiàn)Token認(rèn)證機(jī)制
JWT(JSON Web Token)是一種用于在網(wǎng)絡(luò)上安全地傳輸信息的簡潔的、URL 安全的表示方法,本文主要介紹了Java如何使用JWT實現(xiàn)Token認(rèn)證機(jī)制,需要的可以參考下2024-10-10
java自定義ClassLoader加載指定的class文件操作
這篇文章主要介紹了java自定義ClassLoader加載指定的class文件操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2021-02-02

