2019-06-17 邀请码的生成(基于自增id与多进制方案)
2019-06-17 本文已影响0人
刘明_d589
项目开发过程中,偶尔会有邀请码需求。
1.生成唯一邀请码
2.得到邀请码的用户可以体验某项业务
这里分享一下邀请码的生成方案。
邀请码的要求: 们先明确邀请码需要的格式
邀请码一般由6至10位字符串组成,允许的字符包括大小写字母和数字
邀请码之间不重复
- 只可一次性使用
以上要求,同时满足 1、2 两点要求的实现有一定难度。
方案思路
——— 涉及程序语言部分以JAVA为例
直接使用JAVA UUID 方式生成。
此方法生成的字符串有36位,去除中间的"-"字符,为32位。可以满足要求1,但无法满足要求2.且长度过长。总的来说,可用,但体验不是特别好。
基于体验码自增ID结合多进制字符串生成
在生成体验码时,我们可以依赖于体验码表的自增id来获得邀请码。基于自增id可以保证不会重复,而且关键数据也是最精简的。
直接使用自增id有两个缺陷:
- 1.自增id是纯数字。业务上大多期望是大小写加数字的组合
- 2.自增id容易被破解。直接提供id,攻击者可以猜测并未提供出来的邀请码。
对于问题一,我们可以使用多进制方案,将数字完美映射到要求的字符集。
对于问题二,我们可以将自增的id与随机数结合起来使用。比如使用6位保存id,取4位保存随机数。将两者结合起来生成邀请码。
对于大小写英文字母加数字的情况,可能出现的字符类型有62种(26*2+10),因此我们可以将id和随机数转化为62进制数,0-9a-zA-Z分别表示0-61的值。这样提供出来的邀请码就是在字符上符合要求,生成的邀请码能够保证唯一。同时,唯一因子只有邀请码id,唯一标准非常精简。如果6位用作自增,4位用作随机。那么我们可以保证全局唯一的邀请码个数有 62^6 = 56800235584 ,总共有568亿个。而一个攻击者如果看出来自增段,想要猜下一个邀请码,则需要从 62^4 = 14776336 ,1千4百万种可能中去猜测。
另外,为了让生成的邀请码看起来不是“某几位字符总是相同的”,可以做一些混合,比如将自增段与随机段混合穿插。亦或使用随机段作为key,将自增段做一次等长加密等。
这里提供转换多进制的工具类:
查看工具类实现原文
public class NumericConvertUtils {
/**
* 在进制表示中的字符集合,0-Z分别用于表示最大为62进制的符号表示
*/
private static final char[] digits = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'};
/**
* 将十进制的数字转换为指定进制的字符串
*
* @param number 十进制的数字
* @param seed 指定的进制
* @return 指定进制的字符串
*/
public static String toOtherNumberSystem(long number, int seed) {
if (number < 0) {
number = ((long) 2 * 0x7fffffff) + number + 2;
}
char[] buf = new char[32];
int charPos = 32;
while ((number / seed) > 0) {
buf[--charPos] = digits[(int) (number % seed)];
number /= seed;
}
buf[--charPos] = digits[(int) (number % seed)];
return new String(buf, charPos, (32 - charPos));
}
/**
* 将其它进制的数字(字符串形式)转换为十进制的数字
*
* @param number 其它进制的数字(字符串形式)
* @param seed 指定的进制,也就是参数str的原始进制
* @return 十进制的数字
*/
public static long toDecimalNumber(String number, int seed) {
char[] charBuf = number.toCharArray();
if (seed == 10) {
return Long.parseLong(number);
}
long result = 0, base = 1;
for (int i = charBuf.length - 1; i >= 0; i--) {
int index = 0;
for (int j = 0, length = digits.length; j < length; j++) {
//找到对应字符的下标,对应的下标才是具体的数值
if (digits[j] == charBuf[i]) {
index = j;
}
}
result += index * base;
base *= seed;
}
return result;
}
}
从邀请码的实践中我们可以总结两点:
任何全局唯一的需求,精简下来都是在同一使用场景下全局唯一
对于需要基于十进制数生成特定字符集的需求,均可以基于目标字符集字符个数,映射到对应多进制数