美文网首页
2019-06-17 邀请码的生成(基于自增id与多进制方案)

2019-06-17 邀请码的生成(基于自增id与多进制方案)

作者: 刘明_d589 | 来源:发表于2019-06-17 19:19 被阅读0次

    项目开发过程中,偶尔会有邀请码需求。
    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;
        }
    }
    

    从邀请码的实践中我们可以总结两点:

    • 任何全局唯一的需求,精简下来都是在同一使用场景下全局唯一
    • 对于需要基于十进制数生成特定字符集的需求,均可以基于目标字符集字符个数,映射到对应多进制数

    相关文章

      网友评论

          本文标题:2019-06-17 邀请码的生成(基于自增id与多进制方案)

          本文链接:https://www.haomeiwen.com/subject/ztzofctx.html