转载本文需注明出处: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,数据治理,移动架构原创技术分享。长按二维码关注!