In recent years, the use of QR codes has become more and more popular. The author recently encountered a job that required scanning QR codes to log in to the website, so I studied this mechanism and implemented it with code. After going through the whole process, let’s talk to you about QR code login and other things.
QR Code Principle
QR code was created by WeChat. When WeChat scanned the QR code to log into WeChat, it felt amazing. However, we understand its principle. , it’s not that magical anymore. The QR code actually contains a URL request information through a black and white dot matrix. Scan the code on the terminal, request the URL, and perform the corresponding operation.
The principle of general code scanning operations
WeChat login and Alipay scan code payment are all based on this principle:
1. Request QR code
The desktop side initiates a request for a QR code from the server.
2. Generate a QR code containing a unique id
The desktop will randomly generate an id, which uniquely identifies the QR code for subsequent operations.
3. Scan the QR code on the mobile terminal
Scan the QR code on the mobile terminal and decode the url request in the QR code.
4. The mobile terminal sends a request to the server
The mobile terminal sends a URL request to the server. The request contains two pieces of information. The unique ID identifies which code was scanned, and the specific code in the browser on the terminal. The cookie or header parameters will identify which user scanned the code.
5. The server notifies that the code scan is successful
When the server receives the URL request for the information in the QR code, it notifies the server that the code scan has been successful and adds necessary login cookies and other information. . There are generally several notification methods here: websocket, polling, holding the request until timeout, and polling every few seconds.
The art of url in QR code
How to realize the different performance of scanning code between own client and other clients (such as WeChat)
For example, in business, You may want to do this. If your company's QR code is scanned by other apps (such as WeChat) and you want to jump to a prompt page, there can be a download link for the app on the prompt page; and when it is scanned by yourself When scanned by the app, the corresponding request is made directly.
In this case, you can do it this way. All links in the QR code are encrypted and then processed uniformly with another link.
For example: www.test.com/qr?p=xxxxxx, the p parameter contains the encryption and decryption algorithm agreed between the server and the client (can be symmetric or asymmetric), scan the code on the end to When using this specific path, directly use the decryption algorithm to decrypt the p parameter and obtain www.testqr.com/qrcode?key=s1arV. In this way, a request can be made to the server. However, because other clients do not know this rule, they can only directly Go to request www.test.com/qr?p=xxxxxx, this request returns the prompt page.
How to make QR codes simpler
Many times, we have to have the horse run and the horse not to eat grass. You want the QR code to contain many parameters, but you don’t want the QR code to be too complicated and difficult to scan. At this time, you need to consider how to make the QR code simple without affecting the business.
Agree with the rules on the end: For example, define the i parameter in the encoding information as 1, 2, 3 to represent different URIs, and use the end to match which interface to request when encountering different i parameters
Simplify Everything that can be simplified: simplify the uri, simplify the key and value in the parameters. For example, www.a.com/q?k=s1arV is much simpler than www.abc.def.adfg.edu.com.cn/qrcode/scan?k=77179574e98a7c860007df62a5dbd98b, and the generated QR code is much easier to scan.
Simplify the unique id parameter: in the previous article, the parameter value in the previous request is only 5 digits, and the parameter value in the latter request is the generated 32-bit md5 value. Generating a terminal key is crucial.
Sample code
Generate QR code (remove the white border and add the logo in the middle)
Need to import the jar package: core-2.0.jar of zxing
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 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 = matrix.getEnclosingRectangle(); // 获取二维码图案的属性 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 (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 block e.printStackTrace(); } } }
Generate short links
Basic idea:
The theory of short URL mapping algorithm:
1. Add a long URL to a random number and use the md5 algorithm to generate a 32-bit signature The string is divided into 4 segments, each segment has 8 characters
2. Process these 4 segments in a loop, take the 8 characters of each segment, and treat it as a hexadecimal string with 0x3fffffff (30 bits 1 ) bit AND operation, more than 30 bits are ignored
3. Divide the 30 bits obtained in each segment into 6 segments, and use each 5-digit number as an index of the alphabet to obtain specific characters, and obtain them in sequence 6-digit string;
4. Such an md5 string can obtain four 6-digit strings, and any one of them can be used as the short URL address of this long URL.
5. It is best to use a key-value database for storage. If a collision occurs, replace it with another one. If all four collide, regenerate md5 (because there are random numbers, different md5 will be generated)
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); } } }
The above is the entire content of this article. I hope it will be helpful to everyone's learning. I also hope that everyone will support the PHP Chinese website.
For more Java QR code login process implementation code (including short address generation, including part of the code) related articles, please pay attention to the PHP Chinese website!