近年、QR コードの使用がますます一般的になってきました。筆者は最近、Web サイトにログインするために QR コードをスキャンする必要がある仕事に遭遇したため、このメカニズムを研究し、プロセス全体をコードで実装しました。次にQRコードログインなどについてお話します。
QR コードの原理
QR コードは WeChat によって作成されました。WeChat が Web ページ WeChat にログインするために QR コードをスキャンしたとき、魔法のように感じられましたが、その原理を理解すると、それほど魔法ではありません。 QRコードには、実際には白黒のドットマトリクスでURLリクエスト情報が含まれています。端末上でコードをスキャンし、URL を要求し、対応する操作を実行します。
一般的なコード スキャン操作の原則
WeChat ログインと Alipay スキャン コード支払いはすべて次の原則に基づいています:
1. QR コードをリクエストする
デスクトップ側はサーバーから QR コードのリクエストを開始します。
2. 一意の ID を含む QR コードを生成します
デスクトップは、後続の操作のために QR コードを一意に識別する ID をランダムに生成します。
3.モバイル端末でQRコードをスキャンします
モバイル端末でQRコードをスキャンし、QRコード内のURLリクエストをデコードします。
4. モバイル端末はサーバーにリクエストを送信します。モバイル端末は、スキャンされたコードを識別する 2 つの情報と、特定の Cookie またはヘッダー パラメーターを含みます。クライアントのブラウザでは、どのユーザーがコードをスキャンしたかが識別されます。
5. サーバーはコードスキャンが成功したことを通知します
サーバーはQRコード内の情報のURLリクエストを受信すると、コードスキャンが成功したことをサーバーに通知し、必要なログインCookieとその他の情報を追加します。ここには通常、WebSocket、ポーリング、タイムアウトまでリクエストを保持する、数秒ごとのポーリングなど、いくつかの通知方法があります。
QR コードの URL の芸術
自分のクライアントと他のクライアント (WeChat など) の間でコードをスキャンするパフォーマンスの違いを認識する方法
たとえば、ビジネスでは、次のような操作が必要になることがあります。それはあなたの会社です QR コードが他のアプリ (WeChat など) によってスキャンされ、プロンプト ページにジャンプしたい場合は、プロンプト ページにアプリのダウンロード リンクが表示されることがあります。アプリでは、対応するリクエストを直接作成します。
この場合、QR コード内のすべてのリンクが 1 つのレベルで暗号化され、別のリンクで均一に処理されます。
例: www.test.com/qr?p=xxxxxx、p パラメータには、サーバーとクライアント間で合意された暗号化および復号化アルゴリズム (対称または非対称の可能性があります) が含まれており、これを見つけるために最後にコードをスキャンします。特定のパスの場合、復号アルゴリズムを直接使用して p パラメータを復号し、www.testqr.com/qrcode?key=s1arV を取得できます。ただし、他のクライアントはこれを知りません。ルールでは、www.test.com/qr?p=xxxxxx のみを直接リクエストできます。このリクエストではプロンプト ページが返されます。
QRコードをよりシンプルにする方法
多くの場合、馬を走らせ、馬が草を食べないようにする必要があります。 QR コードには多くのパラメータを含める必要がありますが、QR コードが複雑すぎてスキャンが困難になることは望ましくありません。このとき、ビジネスに影響を与えずにQRコードをシンプルにする方法を検討する必要があります。
エンドポイントとの合意されたルール: たとえば、エンコーディング情報の i パラメーターを 1、2、3 として定義して、異なる URI を表すと、エンドポイントは、異なる i パラメーターに遭遇したときにリクエストするインターフェイスと一致します
簡略化できます: 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パッケージをインポートする必要があります: 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(); } } }
短いリンクを生成します
基本的なアイデア:
短いURLマッピング アルゴリズムの理論:
1. 長い URL を乱数に追加し、md5 アルゴリズムを使用して 4 つのセグメントに分割された 32 ビットの署名文字列を生成します。各セグメントは 8 文字です
2.セグメントをループに分割し、各セグメント文字を 8 個取り、16 進数の文字列と 0x3ffffffff (30 ビット 1) のビットアンド演算として扱い、30 ビットを超える場合は処理を無視します
3。各セグメントは 5 桁の 6 つのセグメントに分割されます。数値は特定の文字を取得するためのアルファベットのインデックスとして使用され、6 桁の文字列が順番に取得されます。このような md5 文字列は 4 つの 6 桁を取得できます。文字列があり、そのいずれかをこの長い URL の短い URL として使用できます。
5. 衝突が発生した場合は、別のデータベースに変更するのが最善です (乱数があるため、別の md5 が生成されます)。以上です。この記事の内容全体が皆さんの学習に役立つことを願っています。また、皆さんが PHP 中国語 Web サイトをサポートしてくれることを願っています。
その他の Java QR コード ログイン処理実装コード (短いアドレス生成、一部のコードを含む) 関連記事については、PHP 中国語 Web サイトに注目してください。