詳解java實(shí)現(xiàn)簡(jiǎn)單掃碼登錄功能(模仿微信網(wǎng)頁(yè)版掃碼)
java實(shí)現(xiàn)簡(jiǎn)單掃碼登錄功能
- 模仿微信pc網(wǎng)頁(yè)版掃碼登錄
- 使用js代碼生成qrcode二維碼減輕服務(wù)器壓力
- js循環(huán)請(qǐng)求服務(wù)端,判斷是否qrcode被掃
- 二維碼超時(shí)失效功能
- 二維碼被掃成功登錄,服務(wù)端產(chǎn)生sessionId,傳到頁(yè)面使用js保存cookie
- 多線(xiàn)程
生成qrcode相關(guān)js jquery.qrcode.js
代碼
頁(yè)面div
<div class="pc_qr_code"> <input type="hidden" id="uuid" value="${uuid }"/> </div> <div id="result">請(qǐng)使用手機(jī)掃碼</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();//自動(dòng)循環(huán)調(diào)用 }(); function keepPool(){ var uuid = $("#uuid").val(); $.get(ctx+"/web/login/pool.do",{uuid:uuid,},function(msg){//如果放入一個(gè)不存在的網(wǎng)址怎么辦? //console.log(msg); if(msg.successFlag == '1'){ $("#result").html("掃碼成功"); setCookie(msg.data.cname, msg.data.cvalue, 3*60*60*1000); //alert("將跳轉(zhuǎn)..."); window.location.href = ctx +"/webstage/login/success.do"; }else if(msg.successFlag == '0'){ $("#result").html("該二維碼已經(jīng)失效,請(qǐng)重新獲取"); }else{ keepPool(); } }); } //設(shè)置cookie function setCookie(cname, cvalue, expireTime) { var d = new Date(); d.setTime(d.getTime() + expireTime);//設(shè)置過(guò)期時(shí)間 var expires = "expires="+d.toUTCString(); var path = "path=/" document.cookie = cname + "=" + cvalue + "; " + expires + "; " + path; }
java代碼
//二維碼首頁(yè) 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("檢測(cè)[ " + uuid + " ]是否登錄"); ScanPool pool = null; if(MapUtils.isNotEmpty(PoolCache.cacheMap)) pool = PoolCache.cacheMap.get(uuid); try { if (pool == null) { // 掃碼超時(shí),進(jìn)線(xiàn)程休眠 result = DataResultInfo.getInstance().failure(); result.setSuccessFlag(CommonConstant.Zero); result.setExtension(CommonConstant.Zero, "該二維碼已經(jīng)失效,請(qǐng)重新獲取"); Thread.sleep(10 * 1000L); } else { // 使用計(jì)時(shí)器,固定時(shí)間后不再等待掃描結(jié)果--防止頁(yè)面訪(fǎng)問(wèn)超時(shí) new Thread(new ScanCounter(uuid, pool)).start(); boolean scanFlag = pool.getScanStatus(); //這里得到的ScanPool(時(shí)間靠前)和用戶(hù)使用手機(jī)掃碼后得到的不是一個(gè),用戶(hù)掃碼后又重新更新了ScanPool對(duì)象,并重新放入了redis中,,所以這里要等待上面的計(jì)時(shí)器走完,才能獲得最新的ScanPool if (scanFlag) { result = DataResultInfo.getSuccess(); // 根據(jù)uuid從redis中獲取pool對(duì)象,得到對(duì)應(yīng)的sessionId,返給頁(yè)面,通過(guò)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); } //手機(jī)掃碼接口(以id和token作為用戶(hù)身份登錄) 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("該二維碼已經(jīng)失效,請(qǐng)重新獲取"); } else { if (StringUtils.isNotEmpty(id) && StringUtils.isNotEmpty(token)) { //根據(jù)id和token查詢(xún)后臺(tái),獲取用戶(hù)信息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); // 添加用戶(hù)信息到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("用戶(hù)信息獲取異常!請(qǐng)稍后再試"); } } else { result = DataResultInfo.getInstance().failure(); result.setExtension("11", "用戶(hù)身份信息失效,請(qǐng)重新登錄!"); } } else { result = DataResultInfo.getInstance().failure(); result.setMessage("請(qǐng)求參數(shù)有誤!"); return "error"; } // 不能清除,否則conn方法得不到pool對(duì)象,不會(huì)進(jìn)入線(xiàn)程休眠 // System.out.println("清除掃描過(guò)的uuid"); //PoolCache.cacheMap.remove(uuid); } } catch (Exception e) { Log4jUtil.CommonLog.error("手機(jī)掃碼 后訪(fǎng)問(wèn) 異常", e); } sendJsonMessage(result); return null; } //掃碼成功跳轉(zhuǎn)頁(yè) 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; } //線(xiàn)程判斷二維碼是否超時(shí) class ScanCounter implements Runnable { public Long timeout = 30 * 1000L; //超時(shí)時(shí)長(zhǎng) // 傳入的對(duì)象 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(用一句話(huà)描述這個(gè)變量表示什么) */ private static final long serialVersionUID = -9117921544228636689L; private Object session ; //創(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),如果還沒(méi)有掃描,則等待固定秒數(shù) * @param wiatSecond 需要等待的秒數(shù) * @return */ public synchronized boolean getScanStatus(){ try { if(!isScan()){ //如果還未掃描,則等待 this.wait(); } if (isScan()) { System.err.println("手機(jī)掃描完成設(shè)置getScanStatus..true..........."); return true; } } catch (InterruptedException e) { e.printStackTrace(); } return false; } /** * 掃碼之后設(shè)置掃碼狀態(tài) * @param token * @param id */ public synchronized void scanSuccess(){ try { System.err.println("手機(jī)掃描完成setScan(true)....同時(shí)釋放notifyAll(手機(jī)掃碼時(shí),根據(jù)uuid獲得的scanpool對(duì)象)"); 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(定時(shí)清理二維碼uuid的類(lèi))
public class PoolCache { // 緩存超時(shí)時(shí)間 10分鐘 private static Long timeOutSecond = 10 * 60 * 1000L; // 每半小時(shí)清理一次緩存 private static Long cleanIntervalSecond = 30 * 60 * 1000L; //此map在多線(xiàn)程中會(huì)出現(xiàn) ConcurrentModificationException //public static Map<String, ScanPool> cacheMap = new HashMap<String, ScanPool>(); //List //public static CopyOnWriteArrayList<Map<String, ScanPool>> copyOnWriteArrayList = new CopyOnWriteArrayList<Map<String,ScanPool>>(); //專(zhuān)用于高并發(fā)的map類(lèi)-----Map的并發(fā)處理(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("定時(shí)清理uuid異常", e); } } }).start(); } }
掃碼流程圖:
流程圖:
使用線(xiàn)程實(shí)時(shí)監(jiān)聽(tīng)掃碼狀態(tài);
用戶(hù)掃描二維碼相當(dāng)于使用 用戶(hù)名密碼 在網(wǎng)頁(yè)端登錄,需要存瀏覽器cookie
,而用戶(hù)通過(guò)使用手機(jī)掃碼,直接請(qǐng)求服務(wù)器,登陸成功,js中得到用戶(hù)數(shù)據(jù)及cookie,把cookie返給頁(yè)面,再通過(guò)js存入cookie中
參考http://www.dbjr.com.cn/article/160745.htm
**應(yīng)大佬們的要求
附上github源碼地址供大家參考*: https://github.com/luuuuuuuuu/qrscan
以上所述是小編給大家介紹的java實(shí)現(xiàn)簡(jiǎn)單掃碼登錄功能(模仿微信網(wǎng)頁(yè)版掃碼)詳解整合,希望對(duì)大家有所幫助,如果大家有任何疑問(wèn)請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)腳本之家網(wǎng)站的支持!
- Java后端接入微信小程序登錄功能(登錄流程)
- java實(shí)現(xiàn)微信掃碼登錄第三方網(wǎng)站功能(原理和代碼)
- 微信小程序微信登錄的實(shí)現(xiàn)方法詳解(JAVA后臺(tái))
- Java中基于Shiro,JWT實(shí)現(xiàn)微信小程序登錄完整例子及實(shí)現(xiàn)過(guò)程
- 使用weixin-java-tools完成微信授權(quán)登錄、微信支付的示例
- java實(shí)現(xiàn)微信小程序登錄態(tài)維護(hù)的示例代碼
- 第三方網(wǎng)站微信登錄java代碼實(shí)現(xiàn)
- java實(shí)現(xiàn) 微博登錄、微信登錄、qq登錄實(shí)現(xiàn)代碼
- Java實(shí)現(xiàn)微信登錄并獲取用戶(hù)信息功能(開(kāi)發(fā)流程)
相關(guān)文章
關(guān)于SpringBoot的異常回滾和事務(wù)的使用詳解
這篇文章主要介紹了關(guān)于SpringBoot的異?;貪L和事務(wù)的使用詳解,Spring中 @Transactional 注解,默認(rèn)情況下,只對(duì)拋出的RuntimeException 異常,才會(huì)事務(wù)回滾,需要的朋友可以參考下2023-05-05在deepin上如何使用Fleet開(kāi)發(fā)SpringBoot?3.0.0項(xiàng)目
這篇文章主要介紹了在deepin上使用Fleet開(kāi)發(fā)SpringBoot?3.0.0項(xiàng)目的過(guò)程,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-09-09SpringBoot項(xiàng)目集成日志的實(shí)現(xiàn)方法
這篇文章主要介紹了SpringBoot項(xiàng)目集成日志的實(shí)現(xiàn)方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2019-02-02Java并發(fā)之ReentrantLock類(lèi)源碼解析
這篇文章主要為大家詳細(xì)介紹了Java并發(fā)系列之ReentrantLock源碼,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-03-03