최근 몇 년 동안 QR 코드의 사용이 점점 더 대중화되었습니다. 저자는 최근 웹사이트에 로그인하기 위해 QR 코드를 스캔해야 하는 작업을 접했고, 이에 대해 연구하고 코드로 구현했습니다. 전체 과정을 통해 QR 코드 로그인 및 기타 사항에 대해 이야기하겠습니다.
QR 코드의 원리
QR 코드는 WeChat에서 개발한 것입니다. WeChat이 QR 코드를 스캔하여 WeChat 웹 페이지에 로그인했을 때 놀라운 느낌이 들었습니다. 더 이상 마법 같은 일이 아닙니다. QR 코드에는 실제로 흑백 도트 매트릭스를 통해 URL 요청 정보가 포함되어 있습니다. 터미널에서 코드를 스캔하고 URL을 요청한 후 해당 작업을 수행합니다.
일반 코드 스캔 작업의 원칙
WeChat 로그인 및 Alipay 스캔 코드 결제는 모두 다음 원칙을 기반으로 합니다.
QR 코드 요청
데스크탑 측은 서버로부터 QR 코드 요청을 시작합니다.
2. 고유 ID가 포함된 QR 코드를 생성합니다.
데스크톱은 후속 작업에서 QR 코드를 고유하게 식별하는 ID를 무작위로 생성합니다.
3. 모바일 단말기에서 QR 코드를 스캔하세요
모바일 단말기에서 QR 코드를 스캔하고 QR 코드에 있는 URL 요청을 디코딩하세요.
4. 모바일 단말기가 서버에 요청을 보냅니다.
모바일 단말기가 서버에 URL 요청을 보냅니다. 요청에는 스캔된 코드를 식별하는 두 가지 정보가 포함됩니다. , 터미널 브라우저의 특정 코드는 쿠키 또는 헤더 매개변수를 통해 코드를 스캔한 사용자를 식별합니다.
5. 서버가 코드 스캔 성공 알림
서버가 QR 코드 정보에 대한 URL 요청을 받으면 코드 스캔이 성공했음을 서버에 알리고 필요한 로그인 쿠키 및 기타 정보를 추가합니다. 여기에는 일반적으로 websocket, 폴링, 시간 초과까지 요청 보류, 몇 초마다 폴링 등 여러 가지 알림 방법이 있습니다.
QR 코드의 URL 기술
자신의 클라이언트와 다른 클라이언트(예: WeChat) 간의 코드 스캔 성능 차이를 실현하는 방법
예를 들어, 회사의 QR 코드를 다른 앱(예: WeChat)에서 스캔하고 프롬프트 페이지로 이동하려는 경우 프롬프트 페이지에 앱 다운로드 링크가 있을 수 있습니다. 직접 스캔합니다. 앱에서 스캔하면 해당 요청이 직접 이루어집니다.
이 경우에는 이렇게 하면 됩니다. QR 코드의 모든 링크는 암호화된 후 다른 링크로 균일하게 처리됩니다.
예: www.test.com/qr?p=xxxxxx, p 매개변수에는 서버와 클라이언트 간에 합의된 암호화 및 암호 해독 알고리즘이 포함되어 있습니다(대칭 또는 비대칭일 수 있음). end to 이 특정 경로를 사용하는 경우 암호 해독 알고리즘을 직접 사용하여 p 매개변수를 디코딩하고 www.testqr.com/qrcode?key=s1arV를 얻습니다. 그러나 다른 클라이언트에 요청이 이루어질 수 있습니다. 이 규칙을 모르면 www.test.com/qr?p=xxxxxx 요청으로 직접 이동할 수만 있습니다. 이 요청은 프롬프트 페이지를 반환합니다.
QR코드를 더 간단하게 만드는 방법
우리는 말을 달려야 하고, 말이 풀을 먹지 말아야 할 때가 많습니다. QR 코드에 많은 매개변수가 포함되기를 원하지만 QR 코드가 너무 복잡하고 스캔하기 어려운 것은 원하지 않습니다. 이때 비즈니스에 영향을 주지 않고 QR코드를 간단하게 만드는 방법을 고민해야 합니다.
마지막 규칙에 동의합니다. 예를 들어 인코딩 정보의 i 매개변수를 1, 2, 3으로 정의하여 서로 다른 URI를 나타내고, 끝은 서로 다른 i 매개변수를 만났을 때 어떤 인터페이스를 요청할지 일치시킵니다.
Simplified 단순화할 수 있는 모든 것: URI를 단순화하고 매개변수의 키와 값을 단순화합니다. 예를 들어 www.a.com/q?k=s1arV는 www.abc.def.adfg.edu.com.cn/qrcode/scan?k=77179574e98a7c860007df62a5dbd98b보다 훨씬 간단하며 생성된 QR 코드는 스캔하기가 훨씬 쉽습니다. .
고유 ID 매개변수를 단순화합니다. 이전 기사에서 이전 요청의 매개변수 값은 5자리에 불과했고, 후자 요청의 매개변수 값은 생성된 32비트 md5 값입니다. 터미널 키를 생성하는 것이 중요합니다.
샘플 코드
QR 코드 생성(흰색 테두리를 제거하고 중간에 로고 추가)
jar 패키지를 가져와야 합니다: zxing's 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 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(); } } }
짧은 링크 생성
기본 아이디어:
짧은 URL 매핑 알고리즘 이론:
1. 긴 URL을 임의의 숫자에 추가하고 사용합니다. 32비트 서명을 생성하는 md5 알고리즘 문자열은 4개의 세그먼트로 나누어지고 각 세그먼트는 8개의 문자를 갖습니다
2. 이 4개의 세그먼트를 루프로 처리하고 각 세그먼트의 8개 문자를 가져와서 처리합니다. 0x3fffffff (30 비트 1 )의 16진수 문자열로, 30을 초과하는 비트는 무시됩니다
3. 각 세그먼트에서 얻은 30비트를 6개의 세그먼트로 나누고, 각 5자리 숫자를 알파벳 인덱스로 사용합니다. 특정 문자를 얻고, 순차적으로 얻으려면 6자리 문자열
4. 이러한 md5 문자열은 4개의 6자리 문자열을 얻을 수 있으며, 그 중 하나를 이의 단축 URL 주소로 사용할 수 있습니다. 긴 URL.
5. 충돌이 발생할 경우 다른 데이터베이스로 변경하여 저장하는 것이 가장 좋습니다. 4개가 모두 충돌하면 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 QR코드 로그인 프로세스 구현 코드(단축 주소 생성 포함, 일부 코드 포함) 관련 글은 PHP 중국어 홈페이지를 주목해주세요!