二维码在企业移动中的应用

6个月前

本文将从应用场景、生成与解析、完整性校验和安全性保证等几个方向阐述二维码在企业中的技术实现和应用。

转载本文需注明出处:微信公众号EAWorld,违者必究。
引言:
二维码是自动识别中的一项重要技术,也是物联网产业的关键、核心技术之一。二维码作为高效的信息载体,结合移动互联技术将人-物相连、物-物相连,并在我们的日常生活中得到了广泛的应用。
近些年来,企业也在以“数据+连接”的思路,尝试着二维码技术在企业场景中的应用和落地。本文将从应用场景、生成与解析、完整性校验和安全性保证等几个方向阐述二维码在企业中的技术实现和应用。
目录:
一、二维码的发展与应用场景
二、二维码管理系统的提出与设计
三、如何实现二维码管理系统
一、二维码的发展与应用场景
  • 二维码的发展
说到二维码我想大家都不会陌生,起初,二维码是日本电装公司为了解决传统条形码信息不能容纳高精度的汽车零配件信息的问题而研究的替代品,这一次升级让原本只能存储20位左右的传统一维条形码一跃成为最多能够存储7089字(仅用数字时)左右的二维码,足足翻了300倍左右。

日本人在当初发明的时候怎么也没有想到,正是这一次不起眼的发明,却在如今成为了中国移动互联网飞速崛起浪潮中的弄潮儿。据《中国互联网络发展状况统计报告》统计,2016年中国人平均每天使用微信扫码就达10亿人次,使用支付宝扫码达到5亿人次。而截止至2018年6月,扫码使用共享单车的用户数量达到2.45亿,占总体网民的30.6%。

  • 企业中的应用场景


随着网络、二维码和二维码读写设备等技术逐渐成熟,企业也在探索二维码在不同场景下的应用。例如:二维码门禁,资产标签,上班打卡,会议签到,员工身份标识,食堂就餐,智慧停车场等。



以企业访客的场景为例,现在各企业间、企业内部各部门之前的合作往来越发密切,为减少访客在拜访排队登记的时间,二维码访客管理应运而生。通过二维码访客管理服务,访客来访之前可以先提交拜访信息给受访人,在受访人核实后由二维码访客管理服务为访客生成对应的访客二维码。访客在来访时可以通过门禁系统的二维码扫描进入具有指定权限的办公区域访问企业用户人员。

再或者以企业组织大型会议为例,组织方往往面临会议通知邀请、会议签到、会议材料发放等等繁杂的流程环节且耗费大量的时间和人工。但在采用二维码会议服务后,会前系统会为每个与会人员发送定制的二维码邀请函,与会人员可以通过扫描二维码邀请函了解会议相关安排信息。到达会议现场后可以通过二维码邀请函进行身份识别,扫码签到,并随时了解会议议程,提升了参会人员的便捷体验感受。而对于会议的组织方来说可以很方便统计会议参会情况并在会后做相关的调查工作。二维码会议服务相对传统会议组织模式来说简化了会议组织流程,减轻了组织方的工作压力的同时提升了效率。

二维码在企业中还有很多类似于上面两个故事的应用范例,不难看出,在企业的管理和发展中,二维码的应用不仅提高了生产效率和管理效率,同时也提升了员工的工作体验。
二、二维码管理系统的提出与设计
  • 二维码管理系统的提出
在企业的发展中,任何技术和系统都是解决企业实际问题而产生的。对企业来讲,尽管二维码有如此宽泛的应用场景,但二维码应该如何去统一管理呢?如果是由各个业务系统去单独完成二维码的处理工作,既会产生大量重复开发工作,又无法有效的统一管理。为此,提出二维码管理系统。按照预定好的内容规范和生成调用规范,为企业各个业务系统提供标准的二维码管理服务,使各业务系统可以更加专注于二维码的实际应用落地。
  • 二维码管理系统的设计
在二维码系统的设计层面,我们既要考虑到使用者调用系统的难度,又要保证系统的安全性和规范性。 
即插即用
二维码内容加密
内容完装性保证
生命期机制
二维码能力SDK易用性
统一二维码标准规范
二维码管理系统是将二维码的生成、验证、加解密、解析等通用能力进行抽象,采用面向对象的设计方法,达到系统的内部高内聚,对外低耦合的目的。以组件化的方式进行封装,充分考虑到使用方的二维码相关能力调用场景,为企业的各个业务系统提供简单易用的二维码能力。 
  • 需要考虑的问题
二维码内容泄露或二维码伪造引起的安全性问题
传输过程中数据篡改引起的完整性问题;
有效期引起的生命期问题。
同时在二维码管理系统的设计,我们需要特别考虑安全性、完成性和生命期的问题,这也是企业二维码系统最基础的要求。为了解决这些问题二维码管理系统会在二维码生成时对二维码内容进行如下处理,首先根据调用业务系统传递的有效期字段生成对应的有效期校验字段按预定规则拼装在二维码内容明文后,然后采用哈希算法(如SM3算法、MD5算法、SHA1算法)对添加过有效期验证字段的二维码明文进行计算来生成对应的完整性校验字段,最后将明文与校验字段组合后按二维码管理系统预设密钥进行对称加密(如DES算哒、AES算法、SM4算法),最终得到用来生成二维码的内容并生成二维码图片返回给业务系统。
二维码生成

在二维码内容解析的时候由业务系统通过扫描设备识别出二维码内容密文,然后将密文传递回二维码管理系统。二维码管理系统在接收到扫描出的二维码密文时先通过之前加密时使用的对称算法的逆算法得到带有校验字段的明文,然后通过之前用来生成校验字段的非对称算法(如SM3算法、MD5算法、SHA1算法)检验明文与检验字段是否符合,保证了二维码完整性的校验,最后在通过在明文中预先拼接的有效期字段进行有效期判断,若过期失效则返回失效信息,反之去除有效期字段返回原始二维码内容。
二维码内容解析

通过上述对二维码内容处理的流程有效地避免了二维码在使用中的大部分问题。
  • 系统场景设计
在设计中我们需要考虑两种场景,主动扫描和被动扫描。
主动扫描

对于主动扫描的场景,用户通过二维码扫描设备扫描二维码获得二维码密文信息,发送至企业的业务系统,由业务系统调用二维码管理系统对密文进行解析、处理,并在获取到解析结果后进行对应的业务操作并反馈给用户最终处理结果。
被动扫描

对于被动扫描的场景,在用户需要被扫码的时候由用户的二维码展示设备发送请求至业务系统,业务系统收到请求后调用二维码管理系统对应的二维码生成服务,生成二维码,并将二维码返回给显示设备,由显示设备显示给扫描方完成被动扫描动作。最后再由扫描方业务系统执行主动扫描场景中的业务处理流程。
主要参考文档:
《QRCODE》
《系统安全规范》
《二维码网格矩阵码(SJ/T 11349-2006)》
《二维码紧密矩阵码(SJ/T 11350-2006)》
《GB/T 18284-2000》 
三、如何实现二维码管理系统
  • 二维码的生成
推荐在二维码标准选择的时候使用应用最为广泛的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,数据治理,移动架构原创技术分享。长按二维码关注!

COMMENTS

需要 后方可回复
如果没有账号可以 一个帐号。