近年来,二维码的使用越来越风生水起,笔者最近手头也遇到了一个需要使用二维码扫码登录网站的活,所以研究了一下这一套机制,并用代码实现了整个流程,接下来就和大家聊聊二维码登录及的那些事儿。

二维码原理

二维码是微信搞起来的,当年微信扫码二维码登录网页微信的时候,感觉很神奇,然而,我们了解了它的原理,也就没那么神奇了。二维码实际上就是通过黑白的点阵包含了一个url请求信息。端上扫码,请求url,做对应的操作。

一般性扫码操作的原理

微信登录、支付宝扫码支付都是这个原理:

如图所示:

1. 请求二维码

桌面端向服务器发起请求一个二维码的。

2. 生成包含唯一id的二维码

桌面端会随机生成一个id,id唯一标识这个二维码,以便后续操作。

3. 端上扫码

移动端扫码二维码,解chu出二维码中的url请求。

4. 移动端发送请求到服务器

移动端向服务器发送url请求,请求中包含两个信息,唯一id标识扫的是哪个码,端上浏览器中特定的cookie或者header参数等会标识由哪个用户来进行扫码的。

5. 服务器端通知扫码成功

服务器端收到二维码中信息的url请求时,通知端上已经扫码成功,并添加必要的登录Cookie等信息。这里的通知方式一般有几种:websocket、轮训hold住请求直到超时、隔几秒轮训。

二维码中url的艺术

如何实现自有客户端和其他客户端扫码(如微信)表现的不同

比如,在业务中,你可能想要这样的操作,如果是你公司的二维码被其他app(如微信)所扫描,想要跳转一个提示页,提示页上可以有一个app的下载链接;而当被你自己的app所扫描时,直接进行对应的请求。

这种情况下,可以这样来做,所有二维码中的链接都进行一层加密,然后统一用另一个链接来处理。

如:www.test.com/qr?p=xxxxxx,p参数中包含服务器与客户端约定的加解密算法(可以是对称的也可以是非对称的),端上扫码到这种特定路径的时候,直接用解密算法解p参数,得到www.testqr.com/qrcode?key=s1arV,这样就可以向服务器发起请求了,而其他客户端因为不知道这个规则,只能直接去请求www.test.com/qr?p=xxxxxx,这个请求返回提示页。

如何让二维码更简单

很多时候,又要马儿跑,又要马儿不吃草。想要二维码中带有很多参数,但是又不想要二维码太复杂,难以被扫码出来。这时候,就需要考虑如何在不影响业务的情况下让二维码变的简单。

  • 与端上约定规则:比如定义编码信息中i参数为1,2,3表示不同的uri,端上来匹配遇到不同的i参数时请求哪个接口

  • 简化一切能简化的地方:简化uri,简化参数中的key、value。如www.a.com/q?k=s1arV就比www.abc.def.adfg.edu.com.cn/qrcode/scan?k=77179574e98a7c860007df62a5dbd98b 要简化很多,生成的二维码要好扫很多。

  • 简化唯一id参数:上一条中前一个请求中参数值只有5位,后一个请求中参数值为生成的32位md5值。生成一个端的key至关重要。

示例代码

生成二维码(去掉白边,增加中间的logo)

需要导入jar包:zxingcore-2.0.jar

import java.awt.BasicStroke;
import java.awt.Color;
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.ByteArrayOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;import javax.imageio.ImageIO;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;public class QrCodeUtil {private static final int BLACK = Color.black.getRGB();private static final int WHITE = Color.WHITE.getRGB();private static final int DEFAULT_QR_SIZE = 183;private static final String DEFAULT_QR_FORMAT = "png";private static final byte[] EMPTY_BYTES = new byte[0];public static byte[] createQrCode(String content, int size, String extension) {return createQrCode(content, size, extension, null);}/*** 生成带图片的二维码* @param content  二维码中要包含的信息* @param size  大小* @param extension  文件格式扩展* @param insertImg  中间的logo图片* @return*/public static byte[] createQrCode(String content, int size, String extension, Image insertImg) {if (size <= 0) {throw new IllegalArgumentException("size (" + size + ")  cannot be <= 0");}ByteArrayOutputStream baos = null;try {Map<EncodeHintType, Object> hints = new HashMap<EncodeHintType, Object>();hints.put(EncodeHintType.CHARACTER_SET, "utf-8");hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.M);//使用信息生成指定大小的点阵BitMatrix m = new MultiFormatWriter().encode(content, BarcodeFormat.QR_CODE, size, size, hints);//去掉白边m = updateBit(m, 0);int width = m.getWidth();int height = m.getHeight();//将BitMatrix中的信息设置到BufferdImage中,形成黑白图片BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);for (int i = 0; i < width; i++) {for (int j = 0; j < height; j++) {image.setRGB(i, j, m.get(i, j) ? BLACK : WHITE);}}if (insertImg != null) {// 插入中间的logo图片insertImage(image, insertImg, m.getWidth());}//将因为去白边而变小的图片再放大image = zoomInImage(image, size, size);baos = new ByteArrayOutputStream();ImageIO.write(image, extension, baos);return baos.toByteArray();} catch (Exception e) {} finally {if(baos != null)try {baos.close();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}return EMPTY_BYTES;}/*** 自定义二维码白边宽度* @param matrix* @param margin* @return*/private static BitMatrix updateBit(BitMatrix matrix, int margin) {int tempM = margin * 2;int[] rec = matrix.getEnclosingRectangle(); // 获取二维码图案的属性int resWidth = rec[2] + tempM;int resHeight = rec[3] + tempM;BitMatrix resMatrix = new BitMatrix(resWidth, resHeight); // 按照自定义边框生成新的BitMatrixresMatrix.clear();for (int i = margin; i < resWidth - margin; i++) { // 循环,将二维码图案绘制到新的bitMatrix中for (int j = margin; j < resHeight - margin; j++) {if (matrix.get(i - margin + rec[0], j - margin + rec[1])) {resMatrix.set(i, j);}}}return resMatrix;}// 图片放大缩小public static BufferedImage zoomInImage(BufferedImage originalImage, int width, int height) {BufferedImage newImage = new BufferedImage(width, height, originalImage.getType());Graphics g = newImage.getGraphics();g.drawImage(originalImage, 0, 0, width, height, null);g.dispose();return newImage;}private static void insertImage(BufferedImage source, Image insertImg, int size) {try {int width = insertImg.getWidth(null);int height = insertImg.getHeight(null);width = width > size / 6 ? size / 6 : width;  // logo设为二维码的六分之一大小height = height > size / 6 ? size / 6 : height;Graphics2D graph = source.createGraphics();int x = (size - width) / 2;int y = (size - height) / 2;graph.drawImage(insertImg, 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();} catch (Exception e) {e.printStackTrace();}}public static byte[] createQrCode(String content) {return createQrCode(content, DEFAULT_QR_SIZE, DEFAULT_QR_FORMAT);}public static void main(String[] args){try {FileOutputStream fos = new FileOutputStream("ab.png");fos.write(createQrCode("test"));fos.close();} catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();}}}
生成短链接

基本思路:

短网址映射算法的理论:

  1. 将长网址加随机数用用md5算法生成32位签名串,分为4段,每段8个字符

  2. 对这4段循环处理,取每段的8个字符, 将他看成16进制字符串与0x3fffffff(30位1)的位与操作,超过30位的忽略处理

  3. 将每段得到的这30位又分成6段,每5位的数字作为字母表的索引取得特定字符,依次进行获得6位字符串;

  4. 这样一个md5字符串可以获得4个6位串,取里面的任意一个就可作为这个长url的短url地址。

  5. 最好是用一个key-value数据库存储,万一发生碰撞换一个,如果四个都发生碰撞,重新生成md5(因为有随机数,会生成不一样的md5)

public class ShortUrlUtil {/*** 传入32位md5值* @param md5* @return*/public static String[] shortUrl(String md5) {// 要使用生成 URL 的字符String[] chars = new String[] { "a", "b", "c", "d", "e", "f", "g", "h","i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t","u", "v", "w", "x", "y", "z", "0", "1", "2", "3", "4", "5","6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "G", "H","I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T","U", "V", "W", "X", "Y", "Z"};String[] resUrl = new String[4];  for (int i = 0; i < 4; i++) {// 把加密字符按照 8 位一组 16 进制与 0x3FFFFFFF 进行位与运算,超过30位的忽略String sTempSubString = md5.substring(i * 8, i * 8 + 8);// 这里需要使用 long 型来转换,因为 Inteper .parseInt() 只能处理 31 位 , 首位为符号位 , 如果不用 long ,则会越界long lHexLong = 0x3FFFFFFF & Long.parseLong(sTempSubString, 16);String outChars = "";for (int j = 0; j < 6; j++) {// 把得到的值与 0x0000003D 进行位与运算,取得字符数组 chars 索引long index = 0x0000003D & lHexLong;// 把取得的字符相加outChars += chars[(int) index];// 每次循环按位右移 5 位lHexLong = lHexLong >> 5;}// 把字符串存入对应索引的输出数组resUrl[i] = outChars;}return resUrl;}public static void main(String [] args){String[] test = shortUrl("fdf8d941f23680be79af83f921b107ac");for (String string : test) {System.out.println(string);}}}

任何人想要转载我的文章,无需和我联系,请转载后把链接私信贴给我,谢谢!

Java二维码登录流程实现(包含短地址生成,含部分代码)相关推荐

  1. java二维码登录实现

    二维码登录原理 让服务端知道是那个用户要登录,验证通过后 服务端通过webscoket 告知 前端 登录成功即可 前端二维码登录实现 链接: 仿知乎pc登录注册二维码登录页面. 下载下来修改一下即可使 ...

  2. 微信QQ的二维码登录原理浅析

    在非常多地方就是都出现了使用二维码登录,二维码付款,二维码账户等应用(这里的二维码种马,诈骗就不说了),二维码验证,多终端辅助授权应用開始多起来,这里先说下啥是二维码,事实上二维码就是存了二进制数据的 ...

  3. 微信QQ的二维码登录原理js代码解析

    这篇文章主要大家详细解析了微信QQ的二维码登录原理js代码, 具有一定的参考价值,感兴趣的小伙伴们可以参考一下 在很多地方就是都出现了使用二维码登录,二维码付款,二维码账户等应用(这里的二维码种马,诈 ...

  4. 微信测试号实现个人第三方PC端网站二维码登录

    这里只提及微信二维码登录PC网站的实现方面,对于微信测试号如何申请,如何授权,本篇博客不去讲解. 测试号申请:http://mp.weixin.qq.com/debug/cgi-bin/sandbox ...

  5. JAVA 实现扫码二维码登录

    最近在做一个扫码登录功能,为此我还在网上搜了一下关于微信的扫描登录的实现方式.当这个功能完成了后,我决定将整个实现思路整理出来,方便自己以后查看也方便其他有类似需求的程序猿些. 要实现扫码登录我们需要 ...

  6. java微信二维码登录

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

  7. 二维码登录原理+Web端即时通讯技术

    前言 上周在写项目过程中遇到需要实现二维码的登录功能,将这个过程细节记录下来 二维码的登录过程,主要难点在于用户扫码了浏览器展示的二维码,但是浏览器本身是无法知道的,需要服务端告知信息. 涉及到 we ...

  8. 让你的网站支持手机二维码登录

    如果你的网站能支持二维码登录, 用户打开手机扫描一下, 网页上就立即变为已登录状态了, 是不是很酷? 像腾讯的微信, 手机淘宝等, 都支持用手机扫描二维码登录 Web 版, 极大的方便了用户. 这项技 ...

  9. Android实现二维码登录的简单实现

    在Android app的开发中,完成一个二维码登录的功能可以帮助我们的了解前后端与Android的简单交互过程,在此做一个简单的登录测试.涉及到简单的PHP的使用以及XAMPP的使用. 1.实现二维 ...

  10. 形象理解二维码登录原理

    之前在极客时间看到一个介绍二维码登录的原理,这里写篇文章记录一下.二维码是我们日常生活中随处可见,支付扫码,登录扫码,添加好友扫码,或者查看某些内容也需要扫码.今天分享一下二维码背后的技术和逻辑,并且 ...

最新文章

  1. linux后台任务部署平台Tac
  2. 使用母版页时内容页如何使用css和javascript
  3. ECCV 2020 | 智能自动零售可行吗?AI安全应引起广泛关注!
  4. Trumb/ARM 指令模式
  5. 我们多么想要新的Java日期/时间API?
  6. 二分法求近似根c语言程序,求一C++风格程序,用二分法求f(x)=0的根
  7. 微软符号服务器 2020年_微软介绍了2020年后它将如何淘汰Edge中的Flash支持
  8. php include的路径问题,php7中include有相对路径无法打开
  9. (36)Verilog HDL关系运算:大于、小于、等于
  10. 万应低代码CTO胡艳平:浅谈低代码在中大型企业数字化转型中的应用
  11. AE光效效果插件:Trapcode Shine
  12. PS入门(1-7) HSB色彩模式
  13. arduino+三引脚蜂鸣器YL-44(3pins)的简单使用
  14. 安卓:点击空白处隐藏软键盘
  15. Go实战--golang中使用JWT(JSON Web Token)
  16. Linux下操作Excel表格,xlsx表格
  17. restrict / __restrict / __restrict__ 关键字
  18. 好用的AndroidStudio插件推荐
  19. flume学习(十一):如何使用Spooling Directory Source
  20. 【C语言编程】古典问题:韩信点兵

热门文章

  1. ppt如何利用视频作为背景
  2. 【NLP】文献翻译2——英语单词语义相似性的Word2Vec模型分析
  3. Assimp库代码存档
  4. Android Studio 中集成 ASSIMP
  5. 你是如何转行的?转行容易吗?
  6. 英语句子划分表示符号使用规则
  7. 人均GDP将破1万美元对中国意味着什么?
  8. 51单片机排队叫号系统LCD1602显示仿真设计(proteus仿真+程序)
  9. 手机变速齿轮_变速齿轮手机版下载|变速齿轮游戏加速器官方最新版v1.2下载 _当游网...
  10. 使用JMeter代理在谷歌浏览器中录制脚本