欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

扫二维码自动跳转【java】详解

程序员文章站 2023-12-05 20:12:52
这个帖子网上很多了,但是都是讲理论知识,我呢,喜欢搞代码。既然搞完了,就贴出来备忘一下,也可以分享一下。 重复理论步骤: 1、进入网站-生成uuid 2、跳转到二维码...

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

重复理论步骤:

1、进入网站-生成uuid

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

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

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

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

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

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

看下微信web的长连接

扫二维码自动跳转【java】详解

扫二维码自动跳转【java】详解

不说了,贴代码了,我这里使用的是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,结束

源码下载地址:

以上所述是小编给大家介绍的java扫二维码自动跳转详解整合,希望对大家有所帮助