轉載本文需註明出處:EAWorld,違者必究。
引言:
二維碼是自動識別中的一項重要技術,也是物聯網產業的關鍵、核心技術之一。二維碼做爲高效的信息載體,結合移動互聯技術將人-物相連、物-物相連,並在咱們的平常生活中獲得了普遍的應用。
近些年來,企業也在以「數據+鏈接」的思路,嘗試着二維碼技術在企業場景中的應用和落地。本文將從應用場景、生成與解析、完整性校驗和安全性保證等幾個方向闡述二維碼在企業中的技術實現和應用。
目錄:
1、二維碼的發展與應用場景
2、二維碼管理系統的提出與設計
3、如何實現二維碼管理系統
1、二維碼的發展與應用場景react
說到二維碼我想你們都不會陌生,起初,二維碼是日本電裝公司爲了解決傳統條形碼信息不能容納高精度的汽車零配件信息的問題而研究的替代品,這一次升級讓本來只能存儲20位左右的傳統一維條形碼一躍成爲最多可以存儲7089字(僅用數字時)左右的二維碼,足足翻了300倍左右。
日本人在當初發明的時候怎麼也沒有想到,正是這一次不起眼的發明,卻在現在成爲了中國移動互聯網飛速崛起浪潮中的弄潮兒。據《中國互聯網絡發展情況統計報告》統計,2016年中國人平均天天使用微信掃碼就達10億人次,使用支付寶掃碼達到5億人次。而截止至2018年6月,掃碼使用共享單車的用戶數量達到2.45億,佔整體網民的30.6%。
jquery
隨着網絡、二維碼和二維碼讀寫設備等技術逐漸成熟,企業也在探索二維碼在不一樣場景下的應用。例如:二維碼門禁,資產標籤,上班打卡,會議簽到,員工身份標識,食堂就餐,智慧停車場等。算法
以企業訪客的場景爲例,如今各企業間、企業內部各部門以前的合做往來愈加密切,爲減小訪客在拜訪排隊登記的時間,二維碼訪客管理應運而生。經過二維碼訪客管理服務,訪客來訪以前能夠先提交拜訪信息給受訪人,在受訪人覈實後由二維碼訪客管理服務爲訪客生成對應的訪客二維碼。訪客在來訪時能夠經過門禁系統的二維碼掃描進入具備指定權限的辦公區域訪問企業用戶人員。安全
再或者以企業組織大型會議爲例,組織方每每面臨會議通知邀請、會議簽到、會議材料發放等等繁雜的流程環節且耗費大量的時間和人工。但在採用二維碼會議服務後,會前系統會爲每一個與會人員發送定製的二維碼邀請函,與會人員能夠經過掃描二維碼邀請函瞭解會議相關安排信息。到達會議現場後能夠經過二維碼邀請函進行身份識別,掃碼簽到,並隨時瞭解會議議程,提高了參會人員的便捷體驗感覺。而對於會議的組織方來講能夠很方便統計會議參會狀況並在會後作相關的調查工做。二維碼會議服務相對傳統會議組織模式來講簡化了會議組織流程,減輕了組織方的工做壓力的同時提高了效率。
二維碼在企業中還有不少相似於上面兩個故事的應用範例,不難看出,在企業的管理和發展中,二維碼的應用不只提升了生產效率和管理效率,同時也提高了員工的工做體驗。
2、二維碼管理系統的提出與設計微信
在企業的發展中,任何技術和系統都是解決企業實際問題而產生的。對企業來說,儘管二維碼有如此寬泛的應用場景,但二維碼應該如何去統一管理呢?若是是由各個業務系統去單獨完成二維碼的處理工做,既會產生大量重複開發工做,又沒法有效的統一管理。爲此,提出二維碼管理系統。按照預約好的內容規範和生成調用規範,爲企業各個業務系統提供標準的二維碼管理服務,使各業務系統能夠更加專一於二維碼的實際應用落地。網絡
在二維碼系統的設計層面,咱們既要考慮到使用者調用系統的難度,又要保證系統的安全性和規範性。 架構
二維碼管理系統是將二維碼的生成、驗證、加解密、解析等通用能力進行抽象,採用面向對象的設計方法,達到系統的內部高內聚,對外低耦合的目的。以組件化的方式進行封裝,充分考慮到使用方的二維碼相關能力調用場景,爲企業的各個業務系統提供簡單易用的二維碼能力。 dom
同時在二維碼管理系統的設計,咱們須要特別考慮安全性、完成性和生命期的問題,這也是企業二維碼系統最基礎的要求。爲了解決這些問題二維碼管理系統會在二維碼生成時對二維碼內容進行以下處理,首先根據調用業務系統傳遞的有效期字段生成對應的有效期校驗字段按預約規則拼裝在二維碼內容明文後,而後採用哈希算法(如SM3算法、MD5算法、SHA1算法)對添加過有效期驗證字段的二維碼明文進行計算來生成對應的完整性校驗字段,最後將明文與校驗字段組合後按二維碼管理系統預設密鑰進行對稱加密(如DES算噠、AES算法、SM4算法),最終獲得用來生成二維碼的內容並生成二維碼圖片返回給業務系統。微服務
二維碼生成工具
在二維碼內容解析的時候由業務系統經過掃描設備識別出二維碼內容密文,而後將密文傳遞迴二維碼管理系統。二維碼管理系統在接收到掃描出的二維碼密文時先經過以前加密時使用的對稱算法的逆算法獲得帶有校驗字段的明文,而後經過以前用來生成校驗字段的非對稱算法(如SM3算法、MD5算法、SHA1算法)檢驗明文與檢驗字段是否符合,保證了二維碼完整性的校驗,最後在經過在明文中預先拼接的有效期字段進行有效期判斷,若過時失效則返回失效信息,反之去除有效期字段返回原始二維碼內容。
二維碼內容解析
經過上述對二維碼內容處理的流程有效地避免了二維碼在使用中的大部分問題。
在設計中咱們須要考慮兩種場景,主動掃描和被動掃描。
主動掃描
對於主動掃描的場景,用戶經過二維碼掃描設備掃描二維碼得到二維碼密文信息,發送至企業的業務系統,由業務系統調用二維碼管理系統對密文進行解析、處理,並在獲取到解析結果後進行對應的業務操做並反饋給用戶最終處理結果。
被動掃描
對於被動掃描的場景,在用戶須要被掃碼的時候由用戶的二維碼展現設備發送請求至業務系統,業務系統收到請求後調用二維碼管理系統對應的二維碼生成服務,生成二維碼,並將二維碼返回給顯示設備,由顯示設備顯示給掃描方完成被動掃描動做。最後再由掃描方業務系統執行主動掃描場景中的業務處理流程。
主要參考文檔:
《QRCODE》
《系統安全規範》
《二維碼網格矩陣碼(SJ/T 11349-2006)》
《二維碼緊密矩陣碼(SJ/T 11350-2006)》
《GB/T 18284-2000》
3、如何實現二維碼管理系統
推薦在二維碼標準選擇的時候使用應用最爲普遍的QRCode碼,後續代碼示例中也會以此爲基準。
目前開源的二維碼生成工具包有不少,好比QRCode、jqueryqrcode、zxing、Barcode4j等。這裏選用一個比較經常使用的由google開發維護的zxing,具體的代碼能夠參考Google代碼測試包中的MatrixToImageWriter類來輔助開發,能夠將該類直接拷貝到源碼中使用。
生成二維碼關鍵代碼範例,以Google的zxing爲例:
public static BufferedImage toBufferedImage(BitMatrix matrix) { int width = matrix.getWidth(); int height = matrix.getHeight(); BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); for (int x = 0; x < width; x++) { for (int y = 0; y < height; y++) { image.setRGB(x, y, (matrix.get(x, y) ? BLACK : WHITE)); } } return image; } public static void writeToFile(BitMatrix matrix, String format, File file) throws IOException { BufferedImage image = toBufferedImage(matrix); LogoConfig logoConfig = new LogoConfig(); image = logoConfig.LogoMatrix(image); if (!ImageIO.write(image, format, file)) { throw new IOException("Could not write an image of format " + format + " to " + file); } }
對於部分業務系統在生成二維碼的時候可能存在須要在二維碼圖片中心添加logo圖片的業務需求,這裏貼出在二維碼中添加logo的示例:
添加logo代碼示例:
public BufferedImage LogoMatrix(BufferedImage matrixImage, String logoFilePath) throws IOException{ Graphics2D g2 = matrixImage.createGraphics(); int matrixWidth = matrixImage.getWidth(); int matrixHeigh = matrixImage.getHeight(); BufferedImage logo = ImageIO.read(new File(logoFilePath)); g2.drawImage(logo,matrixWidth/5*2,matrixHeigh/5*2, matrixWidth/5, matrixHeigh/5, null); BasicStroke stroke = new BasicStroke(5,BasicStroke.CAP_ROUND,BasicStroke.JOIN_ROUND); g2.setStroke(stroke); RoundRectangle2D.Float round = new RoundRectangle2D.Float(matrixWidth/5*2, matrixHeigh/5*2, matrixWidth/5, matrixHeigh/5,20,20); g2.setColor(Color.white); g2.draw(round); BasicStroke stroke2 = new BasicStroke(1,BasicStroke.CAP_ROUND,BasicStroke.JOIN_ROUND); g2.setStroke(stroke2); RoundRectangle2D.Float round2 = new RoundRectangle2D.Float(matrixWidth/5*2+2, matrixHeigh/5*2+2, matrixWidth/5-4, matrixHeigh/5-4,20,20); g2.setColor(new Color(128,128,128)); g2.draw(round2); g2.dispose(); matrixImage.flush() ; return matrixImage ; }
二維碼內容加密主要分爲兩部分,其一是生成用於完整性校驗字段的哈希算法,其二是爲了明文加密的對稱加密。
經常使用於完整性檢驗的哈希算法有不少種,如Ron Rivest(RSA公司)的消息摘要算法MD系列,這其中MD5由於兼具快速和安全兩大特色被普遍採。一樣的哈希算法還有美國國家標準技術研究院(NIST)制定的安全哈希算法SHA(Secure Hash Algorithm)系列算法,國家密碼管理局發佈的雜湊算法標準SM3等。這裏使用MD5做爲代碼範例:
public static String getMD5Text(String data) { MessageDigest messageDigest = null; byte[] srcBytes = data.getBytes(); try { messageDigest = MessageDigest.getInstance("MD5"); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } messageDigest.update(srcBytes, 0, srcBytes.length); BigInteger bigInt = new BigInteger(1, messageDigest.digest()); return bigInt.toString(16); }
在二維碼密文解析的時候可以使用相同方法取原文的MD5值進行檢驗。
二維碼明文加密採用對稱加密算法,對稱密鑰由二維碼管理系統保管,常見的對稱算法有DES算法、AES算法、IDEA算法、SM4算法等,這裏以AES算法爲例。
加密:
public static String encrypt(String content, String password) { try { Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM);// 建立密碼器 byte[] byteContent = content.getBytes("utf-8"); cipher.init(Cipher.ENCRYPT_MODE, getSecretKey(password));// 初始化爲加密模式的密碼器 byte[] result = cipher.doFinal(byteContent);// 加密 return Base64.encodeBase64String(result);//經過Base64轉碼返回 } catch (Exception ex) { Logger.getLogger(AESUtil.class.getName()).log(Level.SEVERE, null, ex); } return null; }
解密:
public static String decrypt(String content, String password) { try { //實例化 Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM); //使用密鑰初始化,設置爲解密模式 cipher.init(Cipher.DECRYPT_MODE, getSecretKey(password)); //執行操做 byte[] result = cipher.doFinal(Base64.decodeBase64(content)); return new String(result, "utf-8"); } catch (Exception ex) { Logger.getLogger(AESUtil.class.getName()).log(Level.SEVERE, null, ex); } return null; }
生成密鑰:
private static SecretKeySpec getSecretKey(final String password) { //返回生成指定算法密鑰生成器的 KeyGenerator 對象 KeyGenerator kg = null; try { kg = KeyGenerator.getInstance(KEY_ALGORITHM); //AES 要求密鑰長度爲 128 kg.init(128, new SecureRandom(password.getBytes())); //生成一個密鑰 SecretKey secretKey = kg.generateKey(); return new SecretKeySpec(secretKey.getEncoded(), KEY_ALGORITHM); // 轉換爲AES專用密鑰 } catch (NoSuchAlgorithmException ex) { Logger.getLogger(AESUtil.class.getName()).log(Level.SEVERE, null, ex); } return null; }
這裏簡要說明了加解密的相關示例,網上也有相關的開源示例,對加解密有興趣的話能夠自行深刻研究。
關於做者:馮浩,現任普元移動團隊開發工程師,畢業於山東大學(威海),主攻移動原生開發、react native開發、JAVA Web開發。前後參與中國郵政集團移動平臺、國家開發銀行移動應用平臺等項目的開發工做。
關於EAWorld:微服務,DevOps,數據治理,移動架構原創技術分享。長按二維碼關注!