当前位置: 移动技术网 > IT编程>开发语言>Java > 详解java实现简单扫码登录功能(模仿微信网页版扫码)

详解java实现简单扫码登录功能(模仿微信网页版扫码)

2019年07月19日  | 移动技术网IT编程  | 我要评论

java实现简单扫码登录功能

  1. 模仿微信pc网页版扫码登录
  2. 使用js代码生成qrcode二维码减轻服务器压力
  3. js循环请求服务端,判断是否qrcode被扫
  4. 二维码超时失效功能
  5. 二维码被扫成功登录,服务端产生sessionid,传到页面使用js保存cookie
  6. 多线程

生成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实现简单扫码登录功能(模仿微信网页版扫码)详解整合,希望对大家有所帮助

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网