詳解java實(shí)現(xiàn)簡單掃碼登錄功能(模仿微信網(wǎng)頁版掃碼)
java實(shí)現(xiàn)簡單掃碼登錄功能
- 模仿微信pc網(wǎng)頁版掃碼登錄
- 使用js代碼生成qrcode二維碼減輕服務(wù)器壓力
- js循環(huán)請求服務(wù)端,判斷是否qrcode被掃
- 二維碼超時(shí)失效功能
- 二維碼被掃成功登錄,服務(wù)端產(chǎn)生sessionId,傳到頁面使用js保存cookie
- 多線程
生成qrcode相關(guān)js jquery.qrcode.js
代碼
頁面div
<div class="pc_qr_code">
<input type="hidden" id="uuid" value="${uuid }"/>
</div>
<div id="result">請使用手機(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)失效,請重新獲取");
}else{
keepPool();
}
});
}
//設(shè)置cookie
function setCookie(cname, cvalue, expireTime) {
var d = new Date();
d.setTime(d.getTime() + expireTime);//設(shè)置過期時(shí)間
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) {
// 掃碼超時(shí),進(jìn)線程休眠
result = DataResultInfo.getInstance().failure();
result.setSuccessFlag(CommonConstant.Zero);
result.setExtension(CommonConstant.Zero, "該二維碼已經(jīng)失效,請重新獲取");
Thread.sleep(10 * 1000L);
} else {
// 使用計(jì)時(shí)器,固定時(shí)間后不再等待掃描結(jié)果--防止頁面訪問超時(shí)
new Thread(new ScanCounter(uuid, pool)).start();
boolean scanFlag = pool.getScanStatus(); //這里得到的ScanPool(時(shí)間靠前)和用戶使用手機(jī)掃碼后得到的不是一個(gè),用戶掃碼后又重新更新了ScanPool對象,并重新放入了redis中,,所以這里要等待上面的計(jì)時(shí)器走完,才能獲得最新的ScanPool
if (scanFlag) {
result = DataResultInfo.getSuccess();
// 根據(jù)uuid從redis中獲取pool對象,得到對應(yīng)的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);
}
//手機(jī)掃碼接口(以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("該二維碼已經(jīng)失效,請重新獲取");
} else {
if (StringUtils.isNotEmpty(id) && StringUtils.isNotEmpty(token)) {
//根據(jù)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("請求參數(shù)有誤!");
return "error";
}
// 不能清除,否則conn方法得不到pool對象,不會(huì)進(jìn)入線程休眠
// System.out.println("清除掃描過的uuid");
//PoolCache.cacheMap.remove(uuid);
}
} catch (Exception e) {
Log4jUtil.CommonLog.error("手機(jī)掃碼 后訪問 異常", e);
}
sendJsonMessage(result);
return null;
}
//掃碼成功跳轉(zhuǎn)頁
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;
}
//線程判斷二維碼是否超時(shí)
class ScanCounter implements Runnable {
public Long timeout = 30 * 1000L; //超時(shí)時(shí)長
// 傳入的對象
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(用一句話描述這個(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),如果還沒有掃描,則等待固定秒數(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對象)");
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的類)
public class PoolCache {
// 緩存超時(shí)時(shí)間 10分鐘
private static Long timeOutSecond = 10 * 60 * 1000L;
// 每半小時(shí)清理一次緩存
private static Long cleanIntervalSecond = 30 * 60 * 1000L;
//此map在多線程中會(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>>();
//專用于高并發(fā)的map類-----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();
}
}
掃碼流程圖:
流程圖:

使用線程實(shí)時(shí)監(jiān)聽掃碼狀態(tài);
用戶掃描二維碼相當(dāng)于使用 用戶名密碼 在網(wǎng)頁端登錄,需要存瀏覽器cookie
,而用戶通過使用手機(jī)掃碼,直接請求服務(wù)器,登陸成功,js中得到用戶數(shù)據(jù)及cookie,把cookie返給頁面,再通過js存入cookie中
參考http://www.dbjr.com.cn/article/160745.htm
**應(yīng)大佬們的要求
附上github源碼地址供大家參考*: https://github.com/luuuuuuuuu/qrscan
以上所述是小編給大家介紹的java實(shí)現(xiàn)簡單掃碼登錄功能(模仿微信網(wǎng)頁版掃碼)詳解整合,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對腳本之家網(wǎng)站的支持!
- Java后端接入微信小程序登錄功能(登錄流程)
- java實(shí)現(xiàn)微信掃碼登錄第三方網(wǎng)站功能(原理和代碼)
- 微信小程序微信登錄的實(shí)現(xiàn)方法詳解(JAVA后臺)
- Java中基于Shiro,JWT實(shí)現(xiàn)微信小程序登錄完整例子及實(shí)現(xiàn)過程
- 使用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)微信登錄并獲取用戶信息功能(開發(fā)流程)
相關(guān)文章
關(guān)于SpringBoot的異?;貪L和事務(wù)的使用詳解
這篇文章主要介紹了關(guān)于SpringBoot的異?;貪L和事務(wù)的使用詳解,Spring中 @Transactional 注解,默認(rèn)情況下,只對拋出的RuntimeException 異常,才會(huì)事務(wù)回滾,需要的朋友可以參考下2023-05-05
在deepin上如何使用Fleet開發(fā)SpringBoot?3.0.0項(xiàng)目
這篇文章主要介紹了在deepin上使用Fleet開發(fā)SpringBoot?3.0.0項(xiàng)目的過程,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-09-09
SpringBoot項(xiàng)目集成日志的實(shí)現(xiàn)方法
這篇文章主要介紹了SpringBoot項(xiàng)目集成日志的實(shí)現(xiàn)方法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2019-02-02

