⼤量兑换码的⽣成与验证⽅案(Java)
由于以前也做过很多兑换码的功能,但是也没有考虑过⼤量的兑换码的情况,所以这⾥尝试实现了⼀个⽣成与兑换的⽅案,分享出来。当然这不⼀定是最好的解决⽅案,这⾥仅供⼤家参考。有什么问题或可以优化的地⽅欢迎留⾔讨论。
*此⽅案可以根据输⼊的兑换码数量以及兑换码的字符长度来⽣成兑换码,每次⽣成兑换码的最⼤数量为int的最⼤值,⼀般为2147483647,即21亿个。
*此⽅案不需要数据库存储已⽣成的兑换码,直接使⽤验证算法即可验证兑换码是否有效。
*注意此算法的最短兑换码长度为12。这⾥可以根据需求对兑换码的结构进⾏调整来减少此长度。
⾸先说下思路
每⼀位兑换码有4部分构成:
[类型(1),id(4),随机码(n),校验码(1)]
类型为每次⽣成兑换码的组id,这⾥只⽤了1个byte来存储,可以根据需要增加。
id为每次⽣成的每个兑换码的唯⼀id。
随机码为每个兑换码的随机数。
校验码⽤来在验证兑换码时进⾏校验。
⽣成过程:
1.根据输⼊的兑换码总长度计算出随机码的位数,然后对每⼀位随机赋值。
2.把类型,id和随机码组成⼀个byte数组,计算总和,然后对byte.max进⾏取余运算,结果最为校验码。把校验码放在数组最末尾。
3.因为前⾯的时间和id有⼀定的规律性,我们使⽤随机码对时间和id进⾏异或操作,使兑换码看起来没有规律。
4.为了更加安全,我们使⽤⼀个密码对全部数据按顺序进⾏异或运算,这样即使知道了算法,在不知道密码的情况些也很难对数据进⾏破解。
5.我们使⽤32位的编码表来表⽰⼆进制数据,但是每个byte位长度为8,能表⽰127个数据,所以这⾥
我们要对原数据进⾏拆分,32位编码表只需要5位就能表⽰,所以这⾥我们把原byte数组拆分为每5位为⼀个byte。如图:
红⾊部分直接补0,末尾不⾜的部分也补充为0.这样⼀位数据就可以正好对应编码表中的⼀个数据。
6.把我们重新整理好的byte数组按照编码表转换位字符串就是我们最终的兑换码。
验证的过程就是把⽣成的过程反过来执⾏⼀遍。
下⾯给出完整代码:
public class Redeem {
public class Redeem {
static String stringtable = "abcdefghijkmnpqrstuvwxyz23456789";
final static String password = "dak3le2";
//从byte转为字符表索引所需要的位数
final static int convertByteCount = 5;
public static void main( String[] args ) throws Exception
{
ShowTime();
System.out.println("=======================");
create((byte)1,10000,12,password);
VerifyCode("c8dksqjamaba");
VerifyCode("4a36g5npamna");
VerifyCode("4a36g5naamna");
VerifyCode("dafe33234g435");
VerifyCode("ga8ehxsq6dja");
}
/**
* ⽣成兑换码
* 这⾥每⼀次⽣成兑换码的最⼤数量为int的最⼤值即2147483647
* @param time
* @param id
* @param count
* @return
*/
public static byte[] create(byte groupid,int codecount,int codelength,String password) {
//8位的数据总长度
int fullcodelength = codelength * convertByteCount / 8;
//随机码对时间和id同时做异或处理
//类型1,id4,随机码n,校验码1
int randcount = fullcodelength - 6;//随机码有多少个
//如果随机码⼩于0 不⽣成
if(randcount <= 0 ) {
return null;
}
for(int i = 0 ; i < codecount ; i ++) {
/
/这⾥使⽤i作为code的id
//⽣成n位随机码
byte[] randbytes = new byte[randcount];
for(int j = 0 ; j  < randcount ; j ++) {
randbytes[j] = (byte)(Math.random() * Byte.MAX_VALUE);
java的tostring方法
}
//存储所有数据
ByteHapper byteHapper = ByteHapper.CreateBytes(fullcodelength);
byteHapper.AppendNumber(groupid).AppendNumber(i).AppendBytes(randbytes);
//计算校验码这⾥使⽤所有数据相加的总和与byte.max 取余
byte verify = (byte) (byteHapper.GetSum() % Byte.MAX_VALUE);
byteHapper.AppendNumber(verify);
//使⽤随机码与时间和ID进⾏异或
for(int j = 0 ; j < 5 ; j ++) {
byteHapper.bytes[j] = (byte) (byteHapper.bytes[j] ^ (byteHapper.bytes[5 + j % randcount]));
}
//使⽤密码与所有数据进⾏异或来加密数据
byte[] passwordbytes = Bytes();
for(int j = 0 ; j < byteHapper.bytes.length ; j++){
byteHapper.bytes[j] = (byte) (byteHapper.bytes[j] ^ passwordbytes[j % passwordbytes.length]);  }
}
//这⾥存储最终的数据
byte[] bytes = new byte[codelength];
//按6位⼀组复制给最终数组
for(int j = 0 ; j < byteHapper.bytes.length ; j ++) {
for(int k = 0 ; k < 8 ; k ++) {
int sourceindex = j*8+k;
int targetindex_x = sourceindex / convertByteCount;
int targetindex_y = sourceindex % convertByteCount;
byte placeval = (byte)Math.pow(2, k);
byte val = (byte)((byteHapper.bytes[j] & placeval) == placeval ? 1:0);
//复制每⼀个bit
bytes[targetindex_x] = (byte)(bytes[targetindex_x] | (val << targetindex_y));    }
}
StringBuilder result = new StringBuilder();
//编辑最终数组⽣成字符串
for(int j = 0 ; j < bytes.length ; j ++) {
result.append(stringtable.charAt(bytes[j]));
}
System.out.println("out string : " + String());
}
ShowTime();
return null;
}
/
**
* 验证兑换码
* @param code
*/
public static void VerifyCode(String code ){
byte[] bytes = new byte[code.length()];
//⾸先遍历字符串从字符表中获取相应的⼆进制数据
for(int i=0;i<code.length();i++){
byte index = (byte) stringtable.indexOf(code.charAt(i));
bytes[i] = index;
}
/
/还原数组
int fullcodelength = code.length() * convertByteCount / 8;
int randcount = fullcodelength - 6;//随机码有多少个
byte[] fullbytes = new byte[fullcodelength];
for(int j = 0 ; j < fullbytes.length ; j ++) {
for(int k = 0 ; k < 8 ; k ++) {
int sourceindex = j*8+k;
int targetindex_x = sourceindex / convertByteCount;
int targetindex_y = sourceindex % convertByteCount;
byte placeval = (byte)Math.pow(2, targetindex_y);
byte val = (byte)((bytes[targetindex_x] & placeval) == placeval ? 1:0);
fullbytes[j] = (byte) (fullbytes[j] | (val << k));
}
}
//解密,使⽤密码与所有数据进⾏异或来加密数据
byte[] passwordbytes = Bytes();
for(int j = 0 ; j < fullbytes.length ; j++){
fullbytes[j] = (byte) (fullbytes[j] ^ passwordbytes[j % passwordbytes.length]);  }
//使⽤随机码与时间和ID进⾏异或
for(int j = 0 ; j < 5 ; j ++) {
fullbytes[j] = (byte) (fullbytes[j] ^ (fullbytes[5 + j % randcount]));  }
//获取校验码计算除校验码位以外所有位的总和
int sum = 0;
for(int i = 0 ;i < fullbytes.length - 1; i ++){
sum += fullbytes[i];
}
byte verify = (byte) (sum % Byte.MAX_VALUE);
//校验
if(verify == fullbytes[fullbytes.length - 1]){
System.out.println(code + " : verify success!");
}else {
System.out.println(code + " : verify failed!");
}
}
public static void ShowTime(){
Date date = new Date();
long times = Time();//时间戳
System.out.println("time  : " + times);
}
}
这⾥为了⽅便还写了⼀个byte[]的辅助类:
public class ByteHapper {
//原始数组
byte[] bytes;
/
/记录当前写⼊到多少位
int index;
private ByteHapper(int capacity){
bytes = new byte[capacity];
index = 0;
}
public static ByteHapper CreateBytes(int capacity){
ByteHapper byteHapper = new ByteHapper(capacity);
return byteHapper;
}
//向数组中追加内容
public ByteHapper AppendNumber(long val){
byte[] arr = Number2byte(val);
AppendBytes(arr);
return this;
}
public ByteHapper AppendNumber(int val){
byte[] arr = Number2byte(val);
AppendBytes(arr);
return this;
}
public ByteHapper AppendNumber(short val){
byte[] arr = Number2byte(val);
AppendBytes(arr);
return this;
}
public ByteHapper AppendNumber(byte val){  byte[] arr = new byte[]{val};
AppendBytes(arr);
return this;
}
/**
* 获取数据的总和
* @return
*/
public int GetSum(){
int ret = 0;
for(int i = 0 ; i < bytes.length ; i ++){
ret += bytes[i];
}
return ret;
}
//追加byte数组
public ByteHapper AppendBytes(byte[] arr){
for(byte i = 0 ; i < arr.length ; i ++){
bytes[index + i] = arr[i];
}
index += arr.length;
return this;
}
/**
* 将数字转换为byte数组
*/
public static byte[] Number2byte(long val) {
byte[] arr = new byte[]{
(byte) ((val >> 56) & 0xFF),
(byte) ((val >> 48) & 0xFF),
(byte) ((val >> 40) & 0xFF),
(byte) ((val >> 32) & 0xFF),
(byte) ((val >> 24) & 0xFF),
(byte) ((val >> 16) & 0xFF),
(byte) ((val >> 8) & 0xFF),
(byte) (val & 0xFF)
};
return arr;
}
public static byte[] Number2byte(int val) {
byte[] arr = new byte[]{
(byte) ((val >> 24) & 0xFF),
(byte) ((val >> 16) & 0xFF),
(byte) ((val >> 8) & 0xFF),
(byte) (val & 0xFF)
};
return arr;
}
public static byte[] Number2byte(short val) {
byte[] arr = new byte[]{
(byte) ((val >> 8) & 0xFF),
(byte) (val & 0xFF)
};

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。