详解java实现简单扫码登录功能(模仿微信网页版扫码)
程序员文章站
2023-12-05 20:04:10
java实现简单扫码登录功能
模仿微信pc网页版扫码登录
使用js代码生成qrcode二维码减轻服务器压力
js循环请求服务端,判断是否qrcode被...
java实现简单扫码登录功能
- 模仿微信pc网页版扫码登录
- 使用js代码生成qrcode二维码减轻服务器压力
- js循环请求服务端,判断是否qrcode被扫
- 二维码超时失效功能
- 二维码被扫成功登录,服务端产生sessionid,传到页面使用js保存cookie
- 多线程
生成qrcode相关js jquery.qrcode.js
代码
页面div
<div class="pc_qr_code"> <input type="hidden" id="uuid" value="${uuid }"/> </div> <div id="result">请使用手机扫码</div>
主要js
//生成二维码 !function(){ var uuid = $("#uuid").val(); var content; content = "..........do?uuid="+uuid; //console.dir(content); $('.pc_qr_code').qrcode({ render:"canvas", width:200, height:200, correctlevel:0, text:content, background:"#ffffff", foreground:"black", src:"/logo.png" }); setcookie("sid", 123, -1*60*60*1000); keeppool();//自动循环调用 }(); function keeppool(){ var uuid = $("#uuid").val(); $.get(ctx+"/web/login/pool.do",{uuid:uuid,},function(msg){//如果放入一个不存在的网址怎么办? //console.log(msg); if(msg.successflag == '1'){ $("#result").html("扫码成功"); setcookie(msg.data.cname, msg.data.cvalue, 3*60*60*1000); //alert("将跳转..."); window.location.href = ctx +"/webstage/login/success.do"; }else if(msg.successflag == '0'){ $("#result").html("该二维码已经失效,请重新获取"); }else{ keeppool(); } }); } //设置cookie function setcookie(cname, cvalue, expiretime) { var d = new date(); d.settime(d.gettime() + expiretime);//设置过期时间 var expires = "expires="+d.toutcstring(); var path = "path=/" document.cookie = cname + "=" + cvalue + "; " + expires + "; " + path; }
java代码
//二维码首页 public string index() { try { uuid = uuid.randomuuid().tostring(); super.getrequest().setattribute("uuid", uuid); scanpool pool = new scanpool(); pool.setcreatetime(system.currenttimemillis()); map<string, scanpool> map = new hashmap<string, scanpool>(1); map.put(uuid, pool); poolcache.cachemap.put(uuid, pool); pool = null; } catch (exception e) { log4jutil.commonlog.error("pc生成二维码登录", e); } return "index"; } //判断二维码是否被扫描 public void pool() { dataresultinfo result = null; system.out.println("检测[ " + uuid + " ]是否登录"); scanpool pool = null; if(maputils.isnotempty(poolcache.cachemap)) pool = poolcache.cachemap.get(uuid); try { if (pool == null) { // 扫码超时,进线程休眠 result = dataresultinfo.getinstance().failure(); result.setsuccessflag(commonconstant.zero); result.setextension(commonconstant.zero, "该二维码已经失效,请重新获取"); thread.sleep(10 * 1000l); } else { // 使用计时器,固定时间后不再等待扫描结果--防止页面访问超时 new thread(new scancounter(uuid, pool)).start(); boolean scanflag = pool.getscanstatus(); //这里得到的scanpool(时间靠前)和用户使用手机扫码后得到的不是一个,用户扫码后又重新更新了scanpool对象,并重新放入了redis中,,所以这里要等待上面的计时器走完,才能获得最新的scanpool if (scanflag) { result = dataresultinfo.getsuccess(); // 根据uuid从redis中获取pool对象,得到对应的sessionid,返给页面,通过js存cookie中 jsonobject jsonobj = new jsonobject(); jsonobj.put("cname", cookieconstant.session_key); jsonobj.put("cvalue", pool.getsession()); result.setdata(jsonobj); } else { result = dataresultinfo.getinstance().failure(); result.setmessage("等待扫描"); } } } catch (exception e) { e.printstacktrace(); } sendjsonmessage(result); } //手机扫码接口(以id和token作为用户身份登录) public string phonescanlogin() { dataresultinfo result = null; scanpool pool = null; if(maputils.isnotempty(poolcache.cachemap)) pool = poolcache.cachemap.get(uuid); try { if (pool == null) { result = dataresultinfo.getinstance().failure(); result.setmessage("该二维码已经失效,请重新获取"); } else { if (stringutils.isnotempty(id) && stringutils.isnotempty(token)) { //根据id和token查询后台,获取用户信息userbean string redistoken = redisutil.getredis(rediskeyconstant.app_token+userid); if(redistoken != null && redistoken.equals(token)){ userbean userbean = userservice.findbyuserid(long.valueof(userid)); if (userbean != null) { string sessionid = sessionconstant.session_id_pre + formatutils.password(userbean.getid() .tostring()); map<string, string> cookiesession = new hashmap<string, string>(); cookiesession .put(cookieconstant.session_key, sessionid); // wrcookie.writecookie(getresponse(),cookiesession); // 添加用户信息到redis boolean re = redisutil.adduserinfo( rediskeyconstant.session + sessionid, beanutils.tobean(userbean, userinfo.class)); getsession().setattribute( sessionconstant.user_info_web, beanutils.tobean(userbean, userinfo.class)); getsession().setattribute( domainconstant.user_center_key, domainconstant.user_center); pool.setsession(sessionid); pool.scansuccess(); }else{ result = dataresultinfo.getinstance().failure(); result.setmessage("用户信息获取异常!请稍后再试"); } } else { result = dataresultinfo.getinstance().failure(); result.setextension("11", "用户身份信息失效,请重新登录!"); } } else { result = dataresultinfo.getinstance().failure(); result.setmessage("请求参数有误!"); return "error"; } // 不能清除,否则conn方法得不到pool对象,不会进入线程休眠 // system.out.println("清除扫描过的uuid"); //poolcache.cachemap.remove(uuid); } } catch (exception e) { log4jutil.commonlog.error("手机扫码 后访问 异常", e); } sendjsonmessage(result); return null; } //扫码成功跳转页 public string success() { string sessionid = wrcookie.getcookie(super.getrequest(), cookieconstant.session_key); userinfo userinfo = redisutil.getuserinfo(rediskeyconstant.session + sessionid); super.getrequest().setattribute(sessionconstant.user_info_web, userinfo); return success; } //线程判断二维码是否超时 class scancounter implements runnable { public long timeout = 30 * 1000l; //超时时长 // 传入的对象 private string uuid; private scanpool scanpool; public scancounter(string p, scanpool scanpool) { uuid = p; this.scanpool = scanpool; } @override public void run() { try { thread.sleep(timeout); } catch (interruptedexception e) { e.printstacktrace(); } notifypool(uuid, scanpool); } public synchronized void notifypool(string uuid, scanpool scanpool) { if (scanpool != null) scanpool.notifypool(); } public string getuuid() { return uuid; } public void setuuid(string uuid) { this.uuid = uuid; } public scanpool getscanpool() { return scanpool; } public void setscanpool(scanpool scanpool) { this.scanpool = scanpool; } }
scanpool.java(存放uuid的bean)
public class scanpool implements serializable{ /** * @fields serialversionuid : todo(用一句话描述这个变量表示什么) */ private static final long serialversionuid = -9117921544228636689l; private object session ; //创建时间 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()) { system.err.println("手机扫描完成设置getscanstatus..true..........."); return true; } } catch (interruptedexception e) { e.printstacktrace(); } return false; } /** * 扫码之后设置扫码状态 * @param token * @param id */ public synchronized void scansuccess(){ try { system.err.println("手机扫描完成setscan(true)....同时释放notifyall(手机扫码时,根据uuid获得的scanpool对象)"); 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; } public object getsession() { return session; } public void setsession(object session) { this.session = session; } }
poolcache.java(定时清理二维码uuid的类)
public class poolcache { // 缓存超时时间 10分钟 private static long timeoutsecond = 10 * 60 * 1000l; // 每半小时清理一次缓存 private static long cleanintervalsecond = 30 * 60 * 1000l; //此map在多线程中会出现 concurrentmodificationexception //public static map<string, scanpool> cachemap = new hashmap<string, scanpool>(); //list //public static copyonwritearraylist<map<string, scanpool>> copyonwritearraylist = new copyonwritearraylist<map<string,scanpool>>(); //专用于高并发的map类-----map的并发处理(concurrenthashmap) public static concurrenthashmap<string, scanpool> cachemap = new concurrenthashmap<string, scanpool>(); static { new thread(new runnable() { @override public void run() { while (true) { try { thread.sleep(cleanintervalsecond); } catch (interruptedexception e) { e.printstacktrace(); } clean(); } } public void clean() { try { /*if (copyonwritearraylist.size() > 0) { iterator<map<string, scanpool>> iterator = copyonwritearraylist.iterator(); while (iterator.hasnext()) { map<string, scanpool> map = iterator.next(); iterator<string> it2 = map.keyset().iterator(); while (it2.hasnext()){ string uuid = it2.next(); scanpool pool = map.get(uuid); if (system.currenttimemillis() - pool.getcreatetime() > timeoutsecond ) { copyonwritearraylist.remove(map); system.err.println("失效了: .. "+ uuid); system.err.println("失效了: .. "+ map); break; } } } }*/ 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 ) { cachemap.remove(key); } } } } catch (exception e) { log4jutil.commonlog.error("定时清理uuid异常", e); } } }).start(); } }
扫码流程图:
流程图:
使用线程实时监听扫码状态;
用户扫描二维码相当于使用 用户名密码 在网页端登录,需要存浏览器cookie
,而用户通过使用手机扫码,直接请求服务器,登陆成功,js中得到用户数据及cookie,把cookie返给页面,再通过js存入cookie中
参考
**应大佬们的要求
附上github源码地址供大家参考*:
以上所述是小编给大家介绍的java实现简单扫码登录功能(模仿微信网页版扫码)详解整合,希望对大家有所帮助