《架构世界》2020移动开发刊:建设移动统一消息管理中心
上QQ阅读APP看书,第一时间看更新

三、如何实现二维码管理系统

二维码的生成

推荐在二维码标准选择的时候使用应用最为广泛的QRCode码,后续代码示例中也会以此为基准。

目前开源的二维码生成工具包有很多,比如QRCodejqueryqrcodezxingBarcode4j等。这里选用一个比较常用的由google开发维护的zxing,具体的代码可以参考Google代码测试包中的MatrixToImageWriter类来辅助开发,可以将该类直接拷贝到源码中使用。

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的示例:

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 RivestRSA公司)的消息摘要算法MD系列,这其中MD5因为兼具快速和安全两大特点被广泛采。同样的哈希算法还有美国国家标准技术研究院(NIST)制定的安全哈希算法SHASecure 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;

}

这里简要说明了加解密的相关示例,网上也有相关的开源示例,对加解密有兴趣的话可以自行深入研究。


推荐阅读

建设移动统一消息管理中心

浅析web端的消息推送原理

React-native如何变为移动端的弄潮儿


关于作者:风行云,现任普元信息移动团队开发工程师,毕业于山东大学(威海),主攻移动原生开发、react native开发、JAVA Web开发。先后参与中国邮政集团移动平台、国家开发银行移动应用平台等项目的开发工作。