开发过程中,经常会生成一段条码来做为一个标识,比如生成几个商品的条码。但如果只是简单的使用几个随机数来标记,没有任何的校验过程,有时可能不太安全。这里介绍一个简单使用的条码校验方法。这样如果条码被修改过,就无法通过校验,从而达到提高安全性的目的。
这里主要使用Luhm 校验算法。Luhm 校验算法广泛用在银行卡号的生成,校验等场景上,这里不多说了。
简单介绍校验的过程:
1、从最后一位数字开始,逆向将奇数位(1、3、5等等)相加。
2、从最后一位数字开始,逆向将偶数位数字,先乘以2(如果乘积为两位数,则将其减去9),再求和。
3、将奇数位总和加上偶数位总和,结果应该可以被10整除。
例如,卡号是:5432123456788881
则奇数、偶数位(用红色标出)分布:5432123456788881
奇数位和=35
偶数位乘以2(有些要减去9)的结果:16 2 6 1 5 7 7,求和=35。
最后35+35=70可以被10整除,认定校验通过。否则则不通过。
这里提供一个工具类,方便大家使用:
/*
* 文件名:CodeNumberUtil.java 版权:Copyright by www.poly.com 描述: 修改人:gogym 修改时间:2018年10月19日 跟踪单号: 修改单号:
* 修改内容:
*/
package com.im.plugin.barcode;
import java.util.Random;
import org.springframework.util.StringUtils;
/**
* 条码生成 采用 Luhm 校验算法
*
* @author gogym
* @version 2018年10月19日
* @see CodeNumberUtil
* @since
*/
public class CodeNumberUtil {
private static int i = 0;
public static final String NUMBERCHAR = "0123456789";
/**
*
* Description: 生成条码(卡号)
*
* @return
* @see
*/
public static String getCodeNumber(String prefix) {
if (!StringUtils.isEmpty(prefix)) {
//可以修改生成随机数的长度,生成想要的长度的条码
String num = generateNumString(5);
String st = prefix + num + getUnixTime();
return st + getCardCheckCode(st);
}
return "prefix不能为空";
}
/**
* 传入一个前缀和一个码。 【根据自己的业务定义】
*
* @param prefix
* @return
*/
public static String getCodeNumber(String prefix, String code) {
if (!StringUtils.isEmpty(prefix) && !StringUtils.isEmpty(code)) {
String st = prefix + code + getUnixTime();
return st + getCardCheckCode(st);
}
return "prefix和code不能为空";
}
/**
* 校验条码是否正确
*
* @param cardId
* @return
*/
public static boolean checkCode(String code) {
char bit = getCardCheckCode(code.substring(0, code.length() - 1));
if (bit == 'N') {
return false;
}
return code.charAt(code.length() - 1) == bit;
}
/**
* 从不含校验位的卡号采用 Luhm 校验算法获得校验位
*
* @param nonCheckCodeCardNo
* @return
*/
private static char getCardCheckCode(String nonCheckCodeCardNo) {
if (nonCheckCodeCardNo == null
|| nonCheckCodeCardNo.trim().length() == 0
|| !nonCheckCodeCardNo.matches("\\d+")) {
// 如果传的不是数据返回N
return 'N';
}
char[] chs = nonCheckCodeCardNo.trim().toCharArray();
int luhmSum = 0;
for (int i = chs.length - 1, j = 0; i >= 0; i--, j++) {
int k = chs[i] - '0';
if (j % 2 == 0) {
k *= 2;
k = k / 10 + k % 10;
}
luhmSum += k;
}
return (luhmSum % 10 == 0) ? '0' : (char) ((10 - luhmSum % 10) + '0');
}
/***
* 获取当前系统时间戳 并截取后8位
*
* @return
*/
private static String getUnixTime() {
try {
Thread.sleep(10);// 快速执行时,休眠10毫秒 防止号码重复
} catch (InterruptedException e) {
e.printStackTrace();
}
i++;
i = i > 100 ? i % 10 : i;
return ((System.currentTimeMillis() / 100) + "").substring(5)
+ (i % 10);
}
/**
* 生成一个定长的纯数字符串
*
* @param length
* 字符串长度
* @return 纯0字符串
*/
private static String generateNumString(int length) {
StringBuffer sb = new StringBuffer();
Random random = new Random();
for (int i = 0; i < length; i++) {
sb.append(NUMBERCHAR.charAt(random.nextInt(NUMBERCHAR.length())));
}
return sb.toString();
}
//测试
public static void main(String[] args) {
try {
for (int i = 0; i < 1; i++) {
// 生成条码
String code = getCodeNumber("18");
System.out.println(code);
// 检验条码
System.out.println(checkCode(code));
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
完