Java⼆维码登录流程实现(包含短地址⽣成,含部分代码)
近年来,⼆维码的使⽤越来越风⽣⽔起,笔者最近⼿头也遇到了⼀个需要使⽤⼆维码扫码登录⽹站的活,所以研究了⼀下这⼀套机制,并⽤代码实现了整个流程,接下来就和⼤家聊聊⼆维码登录及的那些事⼉。
⼆维码原理
⼆维码是搞起来的,当年扫码⼆维码登录⽹页的时候,感觉很神奇,然⽽,我们了解了它的原理,也就没那么神奇了。⼆维码实际上就是通过⿊⽩的点阵包含了⼀个url请求信息。端上扫码,请求url,做对应的操作。
⼀般性扫码操作的原理
登录、⽀付宝扫码⽀付都是这个原理:
如图所⽰:
1. 请求⼆维码
桌⾯端向服务器发起请求⼀个⼆维码的。
2. ⽣成包含唯⼀id的⼆维码
桌⾯端会随机⽣成⼀个id,id唯⼀标识这个⼆维码,以便后续操作。
3. 端上扫码
移动端扫码⼆维码,解chu出⼆维码中的url请求。
4. 移动端发送请求到服务器
移动端向服务器发送url请求,请求中包含两个信息,唯⼀id标识扫的是哪个码,端上浏览器中特定的cookie或者header参数等会标识由哪个⽤户来进⾏扫码的。
5. 服务器端通知扫码成功
服务器端收到⼆维码中信息的url请求时,通知端上已经扫码成功,并添加必要的登录Cookie等信息。这⾥的通知⽅式⼀般有⼏种:websocket、轮训hold住请求直到超时、隔⼏秒轮训。
⼆维码中url的艺术
如何实现⾃有客户端和其他客户端扫码(如)表现的不同
⽐如,在业务中,你可能想要这样的操作,如果是你公司的⼆维码被其他app(如)所扫描,想要跳转⼀个提⽰页,提⽰页上可以有⼀个app的下载链接;⽽当被你⾃⼰的app所扫描时,直接进⾏对应的请求。
这种情况下,可以这样来做,所有⼆维码中的链接都进⾏⼀层加密,然后统⼀⽤另⼀个链接来处理。
如:st/qr?p=xxxxxx,p参数中包含服务器与客户端约定的加解密算法(可以是对称的也可以是⾮对称的),端上扫码到这种特定路径的时候,直接⽤解密算法解p参数,得到stqr/qrcode?key=s1arV,这样就可以向服务器发起请求了,⽽其他客户端因为不知道这个规则,只能直接去请求st/qr?p=xxxxxx,这个请求返回提⽰页。
如何让⼆维码更简单
很多时候,⼜要马⼉跑,⼜要马⼉不吃草。想要⼆维码中带有很多参数,但是⼜不想要⼆维码太复杂,难以被扫码出来。这时候,就需要考虑如何在不影响业务的情况下让⼆维码变的简单。
与端上约定规则:⽐如定义编码信息中i参数为1,2,3表⽰不同的uri,端上来匹配遇到不同的i参数时请求哪个接⼝
简化⼀切能简化的地⽅:简化uri,简化参数中的key、value。如www.a/q?k=s1arV就⽐www.abc.def.adfg.edu/qrcode/scan?
k=77179574e98a7c860007df62a5dbd98b要简化很多,⽣成的⼆维码要好扫很多。
简化唯⼀id参数:上⼀条中前⼀个请求中参数值只有5位,后⼀个请求中参数值为⽣成的32位md5值。⽣成⼀个端的key⾄关重要。
⽰例代码
⽣成⼆维码(去掉⽩边,增加中间的logo)
需要导⼊jar包:zxing的core-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 RoundRectangle2D;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.HashMap;
汉字的来历50字import java.util.Map;
import javax.imageio.ImageIO;
le.zxing.BarcodeFormat;
le.zxing.EncodeHintType;
le.zxing.MultiFormatWriter;
le.zxingmon.BitMatrix;
le.zxing.qrcode.decoder.ErrorCorrectionLevel;
public class QrCodeUtil {
private static final int BLACK = RGB();
private static final int WHITE = RGB();
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);
ByteArray();
} catch (Exception e) {
} finally {
if(baos != null)
try {
baos.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return EMPTY_BYTES;
}
/**
* ⾃定义⼆维码⽩边宽度
* @param matrix
* @param margin
* @return
*/
private static BitMatrix updateBit(BitMatrix matrix, int margin) {
int tempM = margin * 2;
int[] rec = EnclosingRectangle(); // 获取⼆维码图案的属性
int resWidth = rec[2] + tempM;
int resHeight = rec[3] + tempM;
BitMatrix resMatrix = new BitMatrix(resWidth, resHeight); // 按照⾃定义边框⽣成新的BitMatrix resMatrix.clear();
for (int i = margin; i < resWidth - margin; i++) { // 循环,将⼆维码图案绘制到新的bitMatrix中
for (int j = margin; j < resHeight - margin; j++) {
if ((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, Type());
Graphics g = Graphics();
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 = Width(null);
int height = Height(null);
width = width > size / 6 ? size / 6 : width; // logo设为⼆维码的六分之⼀⼤⼩
height = height > size / 6 ? size / 6 : height;
Graphics2D graph = ateGraphics();
int x = (size - width) / 2;2011年9月思想汇报
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 block
e.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);
}
}
}
说明:核⼼代码⾮原创,借鉴了他⼈的代码,感谢!
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论