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

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中

参考https://www.jb51.net/article/160745.htm

**应大佬们的要求
附上github源码地址供大家参考*: https://github.com/luuuuuuuuu/qrscan

以上所述是小编给大家介绍的java实现简单扫码登录功能(模仿微信网页版扫码)详解整合,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对我们网站的支持!

(0)

相关推荐

  • java实现二维码扫码授权登陆

    假设现在有2个设备,A设备需要扫码授权登陆,B设备是已经登陆了的设备.然后实现如下: 一.A设备生成生成二维码: A设备向服务器请求getLoginCode接口,这个接口根据请求的sessionId进行base64或其他加密方式进行加密,然后以此作为二维码的值,并将这个loginCode写到redis里,设置5分钟过期.然后将这个loginCode返回给A设备,A设备以此值来生成登陆的二维码. 二.B设备扫码授权 B设备来扫A设备的二维码的时候,携带二维码的值,请求授权登陆的接口scanConf

  • Java SpringMVC实现PC端网页微信扫码支付(完整版)

    一:前期微信支付扫盲知识 前提条件是已经有申请了微信支付功能的公众号,然后我们需要得到公众号APPID和微信商户号,这个分别在微信公众号和微信支付商家平台上面可以发现.其实在你申请成功支付功能之后,微信会通过邮件把Mail转给你的,有了这些信息之后,我们就可以去微信支付服务支持页面:https://pay.weixin.qq.com/service_provider/index.shtml 打开这个页面,点击右上方的链接[开发文档]会进入到API文档说明页面,看起来如下 选择红色圆圈的扫码支付就

  • java实现微信扫码支付功能

    本文实例为大家分享了java实现微信扫码支付的具体代码,供大家参考,具体内容如下 1.maven项目的pom.xml中添加如下jar包: <dependency> <groupId>com.github.wxpay</groupId> <artifactId>wxpay-sdk</artifactId> <version>0.0.3</version> </dependency> 2.编写WeWxConfig类

  • JAVA微信扫码支付模式一线下支付功能实现

    一.准备工作 无数人来追问模式一的开发,所以在这就贴出来,仅供参考.关于模式一和模式二的区别,我有解释过很多次,无非就是模式一的二维码是针对商品的,模式二的二维码是针对订单的,其他具体细节我就不费口舌了,各位可以自行去官方查看文档,然后是选模式一还是模式二就得看自己的业务了. 1.1.有关配置参数 还是之前那四样,APP_ID和APP_SECRET可以在公众平台找着,MCH_ID和API_KEY则在商户平台找到,特别是API_KEY要在商户平台设置好,这个东东关系到参数校验的正确与否,所以一定要

  • Java微信支付之公众号支付、扫码支付实例

    微信支付现在已经变得越来越流行了,随之也出现了很多以可以快速接入微信支付为噱头的产品,不过方便之余也使得我们做东西慢慢依赖第三方,丧失了独立思考的能力,这次打算分享下我之前开发过的微信支付. 一 .H5公众号支付 要点:正确获取openId以及统一下单接口,正确处理支付结果通知,正确配置支付授权目录 H5的支付方式是使用较为广泛的方式,这种支付方式主要用于微信内自定义菜单的网页,依赖手机上安装的微信客户端,高版本的微信才支持微信支付,下面按我的流程注意说明 1  编写用于支付的页面,由于是测试用

  • java实现在SSM下使用支付宝扫码支付功能

    本文实例为大家分享了java使用支付宝扫码支付的具体代码,供大家参考,具体内容如下 准备工作 首先开通支付宝沙箱的测试账号,里面会有消费者账户和收款方账户 手机扫码下载手机端app 基础配置 所需jar包 AlipayConfig package com.alipay.config; import java.io.FileWriter; import java.io.IOException; import java.util.ResourceBundle; /* * *类名:AlipayConf

  • java实现网站微信扫码支付

    一.网站微信扫码支付开发并没有现成的java示例,总结一下自己微信扫码支付心得 二.首先去微信公众平台申请账户 https://mp.weixin.qq.com ** 三.账户开通.开发者认证之后就可以进行微信支付开发了 1.微信统一下单接口调用获取预支付id,以及生成二维码所需的codeUrl /** * 保存订单,并生成二维码所需的codeUrl * * @param request * @param response * @param notifyURLBuf * @param order

  • javaweb实现app扫码登录功能

    本文为大家分享了javaweb实现app扫码登录的具体代码,供大家参考,具体内容如下 1.web页面主动向服务器索要一张由服务器生成包含维一标识的二维码图片,也可以直接向后台索要一个维一标识,拿到标识后通过js生成二维码.这里本人采用的是第二种方式,至于为什么吗?个人感觉这样方便,后台也不要导入架包,最后将该标识存入List集合中,接下来会用到该标识 2.app扫码后解析二维码内的维一标识,然后再携带该标识跟用户名发回给服务器,服务器接到请求后,遍历List集合,验证该标识是否为本系统生成的,若

  • JAVA微信扫码支付模式二线上支付功能实现以及回调

     一.准备工作 首先吐槽一下微信关于支付这块,本身支持的支付模式就好几种,但是官方文档特别零散,连像样的Java相关的demo也没几个.本人之前没有搞过微信支付,一开始真是被它搞晕,折腾两天终于调通了,特此写下来,以享后人吧! 关于准备工作,就"微信扫码支付模式二"官方文档地址在这 https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=6_1 可以先看看,实际上需要准备的东西有以下几个: 其中APP_ID和APP_SECRE

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

    java实现简单扫码登录功能 模仿微信pc网页版扫码登录 使用js代码生成qrcode二维码减轻服务器压力 js循环请求服务端,判断是否qrcode被扫 二维码超时失效功能 二维码被扫成功登录,服务端产生sessionId,传到页面使用js保存cookie 多线程 生成qrcode相关js jquery.qrcode.js 代码 页面div <div class="pc_qr_code"> <input type="hidden" id="

  • 详解java 拼音首字母搜索内容功能的示例

    序 一款成熟的产品,首页的搜索功能除了正常的关键词匹配以外:还要考虑到用户忘记输入汉字或者用户想通过关键字首字母来进行搜索的操作. 这不,阿淼公司最近在做游戏盒子,其中包含很多游戏,有个需求就是要用户可以根据游戏名称首字母搜索游戏,如搜索 zwdzjs 可以搜索出来植物大战僵尸等:输入 hzw 可以搜索出来海贼王等. 功能如何实现?接下来阿淼就直接带大家实操. 1.导入依赖包 <dependency> <groupId>com.belerweb</groupId> &l

  • Vue PC端实现扫码登录功能示例代码

    目录 需求描述 思路解析 前端功能实现 如何控制二维码的时效性? 前端如何获取服务器二维码的状态? 本篇文章给大家带来了关于Vue的相关知识,其中主要介绍了在PC端实现扫码的原理是什么?怎么生成二维码图片?怎么用Vue实现前端扫码登录?感兴趣的朋友,下面一起来看一下吧,希望对大家有帮助. 需求描述 目前大多数PC端应用都有配套的移动端APP,如微信,淘宝等,通过使用手机APP上的扫一扫功能去扫页面二维码图片进行登录,使得用户登录操作更方便,安全,快捷. 思路解析 PC 扫码原理? 扫码登录功能涉

  • 详解Java动态字节码技术

    对 Debug 的好奇 初学 Java 时,我对 IDEA 的 Debug 非常好奇,不止是它能查看断点的上下文环境,更神奇的是我可以在断点处使用它的 Evaluate 功能直接执行某些命令,进行一些计算或改变当前变量. 刚开始语法不熟经常写错代码,重新打包部署一次代码耗时很长,我就直接面向 Debug 开发.在要编写的方法开始处打一个断点,在 Evaluate 框内一次次地执行方法函数不停地调整代码,没问题后再将代码复制出来放到 IDEA 里,再进行下一个方法的编写,这样就跟写 PHP 类似的

  • 详解Java字节码编程之非常好用的javassist

    一.Javassist入门 (一)Javassist是什么 Javassist是可以动态编辑Java字节码的类库.它可以在Java程序运行时定义一个新的类,并加载到JVM中:还可以在JVM加载时修改一个类文件.Javassist使用户不必关心字节码相关的规范也是可以编辑类文件的. (二)Javassist核心API 在Javassist中每个需要编辑的class都对应一个CtCLass实例,CtClass的含义是编译时的类(compile time class),这些类会存储在Class Poo

  • 详解java实践SPI机制及浅析源码

    1.概念 正式步入今天的核心内容之前,溪源先给大家介绍一下关于SPI机制的相关概念,最后会提供实践源代码. SPI即Service Provider Interface,属于JDK内置的一种动态的服务提供发现机制,可以理解为运行时动态加载接口的实现类.更甚至,大家可以将SPI机制与设计模式中的策略模式建立联系. SPI机制: 从上图中理解SPI机制:标准化接口+策略模式+配置文件: SPI机制核心思想:系统设计的各个抽象,往往有很多不同的实现方案,在面向的对象的设计里,一般推荐模块之间基于接口编

  • 详解Java中ThreadLocal类型及简单用法

    目录 1 基本概念 2 简单使用 3 应用场景 4 底层原理 4.1 set(Object) 4.2 get() 4.3 remove() 4.4 ThreadLocalMap 5 内存泄漏隐患和防止策略 5.1 为什么会发生内存泄漏? 5.2 怎样防止内存泄漏? 1 基本概念 ThreadLocal类提供了线程局部变量.这些变量与普通变量的不同之处在于,每个访问一个变量(通过其get或set方法)的线程都有自己的.独立初始化的变量副本.ThreadLocal实例通常是希望将状态与线程关联起来的

  • 详解Java中的字节码增强技术

    目录 1.字节码增强技术 2.常见技术 3.ASM 3.1 测试 Main 3.2 测试 CustomerClassVisitor 3.3 测试 Test 1.字节码增强技术 字节码增强技术就是一类对现有字节码进行修改或者动态生成全新字节码文件的技术. 参考地址 2.常见技术 技术分类 类型 静态增强 AspectJ 动态增强 ASM.Javassist.Cglib.Java Proxy 3.ASM <dependency> <groupId>org.ow2.asm</gro

  • 详解Java中HashSet和TreeSet的区别

    详解Java中HashSet和TreeSet的区别 1. HashSet HashSet有以下特点: 不能保证元素的排列顺序,顺序有可能发生变化 不是同步的 集合元素可以是null,但只能放入一个null 当向HashSet集合中存入一个元素时,HashSet会调用该对象的hashCode()方法来得到该对象的hashCode值,然后根据 hashCode值来决定该对象在HashSet中存储位置. 简单的说,HashSet集合判断两个元素相等的标准是两个对象通过equals方法比较相等,并且两个

  • 详解java面试题中的i++和++i

    代码如下所示: public class TestPlusPlus{ public static void main(String[] args){ int k = addAfterReturn(10); System.out.println(k); //输出 10 int k1 = addbeforeReturn(10); System.out.println(k1); //输出11 } public static int addbeforeReturn(int i){ return ++i;

随机推荐