一、什么是二维码
二维码又称二维条码,常见的二维码为QR Code,QR全称Quick Response,是一个近几年来移动设备上超流行的一种编码方式,它比传统的Bar Code条形码能存更多的信息,也能表示更多的数据类型。二维条码/二维码(2-dimensional bar code)是用某种特定的几何图形按一定规律在平面(二维方向上)分布的黑白相间的图形记录数据符号信息的;在代码编制上巧妙地利用构成计算机内部逻辑基础的“0”、“1”比特流的概念,使用若干个与二进制相对应的几何形体来表示文字数值信息,通过图象输入设备或光电扫描设备自动识读以实现信息自动处理:它具有条码技术的一些共性:每种码制有其特定的字符集;每个字符占有一定的宽度;具有一定的校验功能等。同时还具有对不同行的信息自动识别功能、及处理图形旋转变化点。

二、二维码生成的几种方式:

  1. 在线二维码生成方式 (http://www.liantu.com/
  2. Java生成二维码使用google zxing 框架
  3. 前端生成二维码使用jquery-qrcode框架

三、扫描登录及跳转原理:
3.1、使用google zxing 框架生成二维码,在二维码中绑定跳转地址
3.2、使用手机扫一扫扫描二维码跳转至地址
3.3 、如何保证二维码唯一性,及超时处理之后怎么处理。
3.3.1、使用token存储至Redis保证二维码的唯一性,
3.3.2、前端使用ajax定时发送请求,检查该token是否为登陆状态

四、扫描二维码登录代码如下:

1.在pom.xml文件中导入相应的jar包:

<!-- 二维码生成jar -->
<dependency><groupId>com.google.zxing</groupId><artifactId>core</artifactId><version>3.3.0</version>
</dependency>
<!-- 集成redis -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- 集成.ftl格式templates模板-->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>

2.在application.yml写入相应的配置如下:

codersWang:baseImgUrl: http://18236a01n6.51mypc.cn:51927/updateTokenState?token=
server:port: 8080
spring:http:encoding:force: truecharset: UTF-8redis:host: 127.0.0.1port: 6379#    password: 123456freemarker:allow-request-override: falsecache: falsecheck-template-location: truecharset: UTF-8content-type: text/html; charset=utf-8expose-request-attributes: falseexpose-session-attributes: falseexpose-spring-macro-helpers: falsesuffix: .ftltemplate-loader-path: classpath:/templates

3.写二维码生成的工具类:

package com.qrcode.code;import java.awt.BasicStroke;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Shape;
import java.awt.geom.RoundRectangle2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.OutputStream;
import java.util.Hashtable;
import java.util.Random;
import javax.imageio.ImageIO;import com.google.zxing.BarcodeFormat;
import com.google.zxing.BinaryBitmap;
import com.google.zxing.DecodeHintType;
import com.google.zxing.EncodeHintType;
import com.google.zxing.MultiFormatReader;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.Result;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.common.HybridBinarizer;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;public class QRCodeUtil {private static final String CHARSET = "utf-8";private static final String FORMAT_NAME = "JPG";// 二维码尺寸private static final int QRCODE_SIZE = 300;// LOGO宽度private static final int WIDTH = 60;// LOGO高度private static final int HEIGHT = 60;public static BufferedImage createImage(String content, String imgPath, boolean needCompress) throws Exception {Hashtable hints = new Hashtable();hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);hints.put(EncodeHintType.CHARACTER_SET, CHARSET);hints.put(EncodeHintType.MARGIN, 1);BitMatrix bitMatrix = new MultiFormatWriter().encode(content, BarcodeFormat.QR_CODE, QRCODE_SIZE, QRCODE_SIZE,hints);int width = bitMatrix.getWidth();int height = bitMatrix.getHeight();BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);for (int x = 0; x < width; x++) {for (int y = 0; y < height; y++) {image.setRGB(x, y, bitMatrix.get(x, y) ? 0xFF000000 : 0xFFFFFFFF);}}if (imgPath == null || "".equals(imgPath)) {return image;}// 插入图片QRCodeUtil.insertImage(image, imgPath, needCompress);return image;}private static void insertImage(BufferedImage source, String imgPath, boolean needCompress) throws Exception {File file = new File(imgPath);if (!file.exists()) {System.err.println("" + imgPath + "   该文件不存在!");return;}Image src = ImageIO.read(new File(imgPath));int width = src.getWidth(null);int height = src.getHeight(null);if (needCompress) { // 压缩LOGOif (width > WIDTH) {width = WIDTH;}if (height > HEIGHT) {height = HEIGHT;}Image image = src.getScaledInstance(width, height, Image.SCALE_SMOOTH);BufferedImage tag = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);Graphics g = tag.getGraphics();g.drawImage(image, 0, 0, null); // 绘制缩小后的图g.dispose();src = image;}// 插入LOGOGraphics2D graph = source.createGraphics();int x = (QRCODE_SIZE - width) / 2;int y = (QRCODE_SIZE - height) / 2;graph.drawImage(src, x, y, width, height, null);Shape shape = new RoundRectangle2D.Float(x, y, width, width, 6, 6);graph.setStroke(new BasicStroke(3f));graph.draw(shape);graph.dispose();}public static void encode(String content, String imgPath, String destPath, boolean needCompress) throws Exception {BufferedImage image = QRCodeUtil.createImage(content, imgPath, needCompress);mkdirs(destPath);// String file = new Random().nextInt(99999999)+".jpg";// ImageIO.write(image, FORMAT_NAME, new File(destPath+"/"+file));ImageIO.write(image, FORMAT_NAME, new File(destPath));}public  BufferedImage encode(String content, String imgPath, boolean needCompress) throws Exception {BufferedImage image = QRCodeUtil.createImage(content, imgPath, needCompress);return image;}public static void mkdirs(String destPath) {File file = new File(destPath);// 当文件夹不存在时,mkdirs会自动创建多层目录,区别于mkdir.(mkdir如果父目录不存在则会抛出异常)if (!file.exists() && !file.isDirectory()) {file.mkdirs();}}public static void encode(String content, String imgPath, String destPath) throws Exception {QRCodeUtil.encode(content, imgPath, destPath, false);}// 被注释的方法/** public static void encode(String content, String destPath, boolean* needCompress) throws Exception { QRCodeUtil.encode(content, null, destPath,* needCompress); }*/public static void encode(String content, String destPath) throws Exception {QRCodeUtil.encode(content, null, destPath, false);}public static void encode(String content, String imgPath, OutputStream output, boolean needCompress)throws Exception {BufferedImage image = QRCodeUtil.createImage(content, imgPath, needCompress);ImageIO.write(image, FORMAT_NAME, output);}public static void encode(String content, OutputStream output) throws Exception {QRCodeUtil.encode(content, null, output, false);}public static String decode(File file) throws Exception {BufferedImage image;image = ImageIO.read(file);if (image == null) {return null;}BufferedImageLuminanceSource source = new BufferedImageLuminanceSource(image);BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));Result result;Hashtable hints = new Hashtable();hints.put(DecodeHintType.CHARACTER_SET, CHARSET);result = new MultiFormatReader().decode(bitmap, hints);String resultStr = result.getText();return resultStr;}public static String decode(String path) throws Exception {return QRCodeUtil.decode(new File(path));}}
package com.qrcode.code;import java.awt.Graphics2D;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;import com.google.zxing.LuminanceSource;public class BufferedImageLuminanceSource extends LuminanceSource {private final BufferedImage image;private final int left;private final int top;public BufferedImageLuminanceSource(BufferedImage image) {this(image, 0, 0, image.getWidth(), image.getHeight());}public BufferedImageLuminanceSource(BufferedImage image, int left, int top, int width, int height) {super(width, height);int sourceWidth = image.getWidth();int sourceHeight = image.getHeight();if (left + width > sourceWidth || top + height > sourceHeight) {throw new IllegalArgumentException("Crop rectangle does not fit within image data.");}for (int y = top; y < top + height; y++) {for (int x = left; x < left + width; x++) {if ((image.getRGB(x, y) & 0xFF000000) == 0) {image.setRGB(x, y, 0xFFFFFFFF); // = white}}}this.image = new BufferedImage(sourceWidth, sourceHeight, BufferedImage.TYPE_BYTE_GRAY);this.image.getGraphics().drawImage(image, 0, 0, null);this.left = left;this.top = top;}public byte[] getRow(int y, byte[] row) {if (y < 0 || y >= getHeight()) {throw new IllegalArgumentException("Requested row is outside the image: " + y);}int width = getWidth();if (row == null || row.length < width) {row = new byte[width];}image.getRaster().getDataElements(left, top + y, width, 1, row);return row;}public byte[] getMatrix() {int width = getWidth();int height = getHeight();int area = width * height;byte[] matrix = new byte[area];image.getRaster().getDataElements(left, top, width, height, matrix);return matrix;}public boolean isCropSupported() {return true;}public LuminanceSource crop(int left, int top, int width, int height) {return new BufferedImageLuminanceSource(image, this.left + left, this.top + top, width, height);}public boolean isRotateSupported() {return true;}public LuminanceSource rotateCounterClockwise() {int sourceWidth = image.getWidth();int sourceHeight = image.getHeight();AffineTransform transform = new AffineTransform(0.0, -1.0, 1.0, 0.0, 0.0, sourceWidth);BufferedImage rotatedImage = new BufferedImage(sourceHeight, sourceWidth, BufferedImage.TYPE_BYTE_GRAY);Graphics2D g = rotatedImage.createGraphics();g.drawImage(image, transform, null);g.dispose();int width = getWidth();return new BufferedImageLuminanceSource(rotatedImage, top, sourceWidth - (left + width), getHeight(), width);}}

4.Redis工具类:

package com.qrcode.token;import java.util.List;
import java.util.concurrent.TimeUnit;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;@Component
public class RedisUtil {@Autowiredprivate StringRedisTemplate stringRedisTemplate;// 如果key存在的话返回fasle 不存在的话返回truepublic Boolean setNx(String key, String value, Long timeout) {Boolean setIfAbsent = stringRedisTemplate.opsForValue().setIfAbsent(key, value);if (timeout != null) {stringRedisTemplate.expire(key, timeout, TimeUnit.SECONDS);}return setIfAbsent;}public StringRedisTemplate getStringRedisTemplate() {return stringRedisTemplate;}public void setList(String key, List<String> listToken) {stringRedisTemplate.opsForList().leftPushAll(key, listToken);}/*** 存放string类型* * @param key*            key* @param data*            数据* @param timeout*            超时间*/public void setString(String key, String data, Long timeout) {try {stringRedisTemplate.opsForValue().set(key, data);if (timeout != null) {stringRedisTemplate.expire(key, timeout, TimeUnit.SECONDS);}} catch (Exception e) {}}/*** 开启Redis 事务* * @param*/public void begin() {// 开启Redis 事务权限stringRedisTemplate.setEnableTransactionSupport(true);// 开启事务stringRedisTemplate.multi();}/*** 提交事务* * @param*/public void exec() {// 成功提交事务stringRedisTemplate.exec();}/*** 回滚Redis 事务*/public void discard() {stringRedisTemplate.discard();}/*** 存放string类型* * @param key*            key* @param data*            数据*/public void setString(String key, String data) {setString(key, data, null);}/*** 根据key查询string类型* * @param key* @return*/public String getString(String key) {String value = stringRedisTemplate.opsForValue().get(key);return value;}/*** 根据对应的key删除key* * @param key*/public Boolean delKey(String key) {return stringRedisTemplate.delete(key);}
}
package com.qrcode.token;import java.util.ArrayList;
import java.util.List;
import java.util.UUID;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;@Component
public class GenerateToken {@Autowiredprivate RedisUtil redisUtil;/*** 生成令牌** @param keyPrefix*            令牌key前缀* @param redisValue*            redis存放的值* @return 返回token*/public String createToken(String keyPrefix, String redisValue) {return createToken(keyPrefix, redisValue, null);}/*** 生成令牌** @param keyPrefix*            令牌key前缀* @param redisValue*            redis存放的值* @param time*            有效期* @return 返回token*/public String createToken(String keyPrefix, String redisValue, Long time) {if (StringUtils.isEmpty(redisValue)) {new Exception("redisValue Not nul");}String token = keyPrefix + UUID.randomUUID().toString().replace("-", "");redisUtil.setString(token, redisValue, time);return token;}public void createListToken(String keyPrefix, String redisKey, Long tokenQuantity) {List<String> listToken = getListToken(keyPrefix, tokenQuantity);redisUtil.setList(redisKey, listToken);}public List<String> getListToken(String keyPrefix, Long tokenQuantity) {List<String> listToken = new ArrayList<>();for (int i = 0; i < tokenQuantity; i++) {String token = keyPrefix + UUID.randomUUID().toString().replace("-", "");listToken.add(token);}return listToken;}public String getListKeyToken(String key) {String value = redisUtil.getStringRedisTemplate().opsForList().leftPop(key);return value;}/*** 根据token获取redis中的value值** @param token* @return*/public String getToken(String token) {if (StringUtils.isEmpty(token)) {return null;}String value = redisUtil.getString(token);return value;}/*** 移除token** @param token* @return*/public Boolean removeToken(String token) {if (StringUtils.isEmpty(token)) {return null;}return redisUtil.delKey(token);}}

5.返回前端的controller层

package com.qrcode.controller;import com.qrcode.code.QRCodeUtil;
import com.qrcode.token.RedisUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;import javax.servlet.http.HttpServletRequest;
import java.util.UUID;/*** @author EDZ* @date 2019/5/28*/
@Controller
public class LoginController {@Autowiredprivate RedisUtil redisUtil;@Value("${codersWang.baseImgUrl}")private String baseImgUr;@RequestMapping("/qc")public String index(HttpServletRequest request) throws Exception {//获取tokenString token = getToken();request.setAttribute("token", token);return "index";
}@RequestMapping("/success")public String success() {return "success";}@RequestMapping("/updateTokenState")public String updateTokenState(String token) {String redisToken = redisUtil.getString(token);if (StringUtils.isEmpty(redisToken) || redisToken.equals("1")) {return "failure";}// 将该token的状态改为1redisUtil.setString(token, "1");return "success";}@RequestMapping("/failure")public String failure() {return "failure";}//生成token,生成二维码private String getToken() throws Exception {// 1.使用UUID生成token替换掉 "-"String token = UUID.randomUUID().toString().replace("-", "");// 2. 存放在redis中 0 表示没有被扫过,1表示已经扫过redisUtil.setString(token, "0",20000L);// 3.1存放在二维码中的内容// 3.2嵌入二维码的图片路径String imgPath = "G:/aa.jpg";// 3.3生成的二维码的路径及名称String destPath = "G:/aa" + token + ".jpg";// 4.生成二维码QRCodeUtil.encode(baseImgUr + token, imgPath, destPath, true);// 5.解析二维码QRCodeUtil.decode(destPath);return token;}
}

6.访问接口  生成二维码返回前端   访问地址:http://localhost:8080/qc

7.扫描以上二维码会跳转登录成功页  如下:

8.前端的相应页面代码如下:
index.ftl页面:

<h1>扫一下该二维码,实现扫码登陆</h1><img src="getImg?token=${token}">
<script src="http://code.jquery.com/jquery-latest.js"></script>
<script>function checkToken() {$.ajax({url: "checkToken?token=${token}",dataType: "json",type: "get",async: "false",success: function (data) {if (data == true) {window.location.href = '/success';}} ,error: function () {}});}setInterval(checkToken, 2000);//定时请求Redis中的状态是否改变</script>

success.ftl页面:

<h1>六一儿童节快乐</h1>

failure.ftl页面:

<h1>Token已过期!</h1>.

9.注意 以上代码是在本地调试    微信和本地不在一个网段  所以需要用到内网渗透   本人用的是花生壳    将本地端口映射到花生壳上即可  如下:

扫描微信二维码实现快速登录相关推荐

  1. 开放平台–扫描微信二维码登录

    准备 如不了解第三方登录流程,建议先大概了解一下,在来看看代码. 说明: 由于开放平台无测试号测试,所以只能上开放平台进行配置信息.公众平台的测试号并不能给开放平台使用. 微信开放平台地址:https ...

  2. 微信二维码支付快速入门

    目录 一.二维码生成插件qrious 二.HttpClient 三.微信扫码支付 1.申请步骤 2.开发文档 四.入门Demo 1.工程搭建 2.myStudy-pay-interface 3.myS ...

  3. 微信小程序 扫描微信二维码 传递参数授权

    首先 先用微信小程序扫一扫 看二维码的参数是什么 然后 扫描二维码的参数是  scene=u_127 这个就是小程序二维码的参数 能够在 页面里拿到 扫描微信小程序授权的结果 可以进行编译 能够在on ...

  4. android扫描网页二维码进行网页登录

    转载请标明出处: http://www.cnblogs.com/dingxiansen/: 本文出自:丁先森-博客园 周六和朋友去网吧开黑,开机打开TGP,朋友那边开始输入账号密码,我看了他一眼low ...

  5. 最近在用uniapp做一款app软件,兼容在小程序中可以通过扫描微信二维码获取参数

    功能描述 该接口用于获取小程序码,适用于需要的码数量极多的业务场景.通过该接口生成的小程序码,永久有效,数量暂无限. 注意事项 如果调用成功,会直接返回图片二进制内容,如果请求失败,会返回 JSON ...

  6. 小程序获取企业微信二维码,使用联系我插件配置企业微信二维码

    通过配置获取企业微信二维码总共分为五步: 第一步:登录企业微信管理后台,查询企业微信的企业ID(corpid)和Secret(corpsecret); 第二步:获取access_token: 第三步: ...

  7. 【微信公众号】微信集成功能--扫描二维码完成用户登录操作

    目录 需求来源 实现思路 1.进入登录页面,生成微信公众号的临时二维码: 2.用户通过微信扫一扫二维码: 3.登录页面定时查询扫码结果: 代码实现(基于Laravel框架前后端混合) HTML PHP ...

  8. 微信二维码扫描登录原理解析

    扫二维码登录现在比较常见,比如微信.支付宝等 PC 端登录,并且好像每款 APP 都支持扫码登录. 一,传统项目移动端基于 token 的认证机制 在了解扫码登录原理之前,有必要先了解移动端基于 to ...

  9. 对于微信二维码相关官方文档的一些注解(微信登录和绑定微信、关注公众号)

    转载自:https://www.jianshu.com/p/d533c69be034 由于微信官方文档对此的描述虽然还可以,但是还是有一些让人疑惑的地方,所以笔者做了一些注解,希望对大家有所帮助 为什 ...

  10. java微信二维码登录

    1.注册 微信开放平台:https://open.weixin.qq.com 2.邮箱激活 3.完善开发者资料 4.开发者资质认证 准备营业执照,1-2个工作日审批.300元 5.创建网站应用 提交审 ...

最新文章

  1. 人脸识别必读的N篇文章
  2. myeclipse下hibernate入门实例介绍
  3. DockerONE 干货 深入理解Docker容器和镜像
  4. 神牛笔记:吉林大学ACM总结(fennec)
  5. 2019年GitHub上最热门的Java开源项目
  6. 08-mysql-条件查询-常见函数与小结
  7. h:commandButton
  8. 查询oracle数据库的表格数据类型,excel表格中如何查询数据库数据类型-我想把excel表格中的数据导入oracle数据库中,想在......
  9. u盘分区变为raw,提示使用前需要将其格式化
  10. PS 如何去除光晕的黑色背景
  11. 破晓博客-自定义标签的开发
  12. 详细介绍MVC与Django的MVT模式
  13. 2022小美赛认证杯数学建模D题完整原创成品来啦!
  14. 大张伟侮辱了恩克,优酷侮辱了世界杯
  15. 用户层调用nl80211的例子
  16. 领导驾驶舱如何助力领导做决策?
  17. 计算方法实验(五):高斯列主元消去法
  18. 电解电容、钽电容、普通电容
  19. Java黑皮书课后题第4章:*4.24(对三个城市排序)编写一个程序,提示用户输入三个城市名称,然后以升序进行显示
  20. 如何在项目中区分使用opencv3和opencv4而不会产生冲突

热门文章

  1. vs2008中文版提供下载(包含中文msdn)
  2. oracle 认证视频,Oracle 认证专家视频教程-OCP全套教程【98集】_IT教程网
  3. python图片分析中央气象台降水_python 画降水量色斑图问题
  4. Java开发实战经典学习记录(一)
  5. XCode编译器里有鬼 – XCodeGhost样本分析
  6. 谷歌Pixel3安装刷入第三方recovery twrp教程
  7. 听音扒谱app_识字APP评测!洪恩、熊猫博士、麦田、悟空、叫叫哪个好?
  8. 安卓抓包工具tcpdump
  9. IE降级命令(以IE11为例)_原水_新浪博客
  10. 二级java考什么_计算机二级Java考试考什么内容?