8、9位用户ID生成加密ID、解密算法

我们可能需要对某些用户的真实数字ID进行加密,让加密后的ID长度还是那么多,又要可恢复,可一眼认出是个加密ID,因此我设计了一种加解密方法,可对8、9位数字ID做加密,加密后的ID长度9位,包含一个字母,其余均是数字,且无法发现加密前后两ID的规律,加密工具如下。

System.out.println(UserIdEncodeUtil.encodeID(52113075L));
System.out.println(UserIdEncodeUtil.encodeID(92113075L));
System.out.println(UserIdEncodeUtil.encodeID(521130751L));
System.out.println(UserIdEncodeUtil.encodeID(921130751L));

u80768999
N59508377
1u8076899
1N5950837





import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 8、9位数字ID加密、解密工具
 *
 * @author humorchen
 * @date 2022/3/16 13:48
 */
public class UserIdEncodeUtil {
    /**
     * 最小ID(8位)
     */
    private static int MIN_ID = 10000000;
    /**
     * 最大ID(9亿9)
     */
    private static int MAX_ID = 999999999;
    /**
     * 最多26*2=52个字母
     */
    private static char[] CHARS = new char[52];
    /**
     * 不使用的字母集合
     */
    private static List<Character> BAN_CHARS = Arrays.asList('O', 'o', 'l');
    /**
     * 字母和字母数组下标的映射
     */
    private static Map<Character, Integer> CHAR_INDEX_MAP = new HashMap<>(52);

    /**
     * 初始化字符数组和映射map
     */
    static {
        fillAlphabet('a', 'z');
        fillAlphabet('A', 'Z');
    }

    /**
     * 从form到to的字符填充到数组和映射map里去
     *
     * @param from
     * @param to
     */
    private static void fillAlphabet(char from, char to) {
        for (int i = from; i <= to; i++) {
            char c = (char) i;
            if (BAN_CHARS.contains(c)) {
                continue;
            }
            int index = CHAR_INDEX_MAP.size();
            CHARS[index] = c;
            CHAR_INDEX_MAP.put(c, index);
        }
    }

    /**
     * 编码ID数字
     *
     * @param n
     * @return
     */
    private static int encode(int n) {
        int ret = 0;
        if (n < 16777217) {
            n = (n << 2) | 3;
        } else {
            n = n << 2 | 1;
        }
        while (n > 0) {
            ret = (ret << 1) | (n & 1);
            n = n >> 1;
        }
        return ret;
    }

    /**
     * 解码ID的数字
     *
     * @param n
     * @return
     */
    private static int decode(int n) {
        int ret = 0;
        while (n > 0) {
            ret = (ret << 1) | (n & 1);
            n = n >> 1;
        }
        ret = ret >> 2;
        return ret;
    }

    /**
     * 编码后的数字转换为字符串显示
     *
     * @param n
     * @return
     */
    private static String nToString(int n) {
        StringBuilder sb = new StringBuilder();
        sb.append(n);
        if (sb.length() == 9) {
            //有9位,则前面两位数字做字符值小写、大写52个位置
            sb.delete(0, 2);
            int i = n / 10000000;
            sb.insert(0, CHARS[i]);
        } else {
            //8位数,最后一位变d+最前面第一个数字,第一位就变字母了,还原的时候字符值-d即可(d是100)
            int i = n % 10;
            sb.setCharAt(7, (char) ('d' + i));
        }
        return sb.toString();
    }

    /**
     * 编码后的ID转换回被加密的数字
     *
     * @param id
     * @return
     */
    private static int stringToN(String id) {
        char fist = id.charAt(0);
        char last = id.charAt(id.length() - 1);
        int n = -1;
        if (fist >= 'A') {
            //是第一个为字母
            n = CHAR_INDEX_MAP.get(fist) * 10000000 + Integer.parseInt(id.substring(1));
        } else if (last >= 'a') {
            //最后一个位字母(去掉最后一位变数字乘以10+最后一位的数字)
            n = Integer.parseInt(id.substring(0, id.length() - 1)) * 10 + last - 'd';
        }
        return n;
    }


    /**
     * 编码8位数字ID
     *
     * @param n
     * @return
     */
    private static String encode8ID(int n) {
        return nToString(encode(n));
    }

    /**
     * 解码8位ID
     *
     * @param n
     * @return
     */
    private static int decode8ID(String n) {
        return decode(stringToN(n));
    }

    /**
     * 加密8、9位数字ID
     *
     * @param id
     * @return
     */
    public static String encodeID(Long id) {
        //参数校验
        if (id == null || id < MIN_ID || id > MAX_ID) {
            return "";
        }
        int n = id.intValue();
        int nine = 100000000;
        //9位ID
        StringBuilder sb = new StringBuilder(9);
        //九位ID
        if (n >= nine) {
            //取前8位
            int eight = n / 10;
            //最后一位数
            int last = n % 10;
            //加密前8位
            String eightID = encode8ID(eight);
            sb.append(eightID);
            if (eightID.charAt(0) >= 'A') {
                //第一位是字母
                sb.insert(0, last);
            } else {
                //最后一位是字母
                sb.append(last);
            }
        } else {
            //八位ID
            String eightID = encode8ID(n);
            sb.append(eightID);
            if (eightID.charAt(0) >= 'A') {
                //第一位是字母则补最后一位
                sb.append(eightID.charAt(eightID.length() - 1));
            } else {
                //最后一位是字母就补第一位
                sb.insert(0, eightID.charAt(0));
            }
        }

        return sb.toString();
    }


    /**
     * 解码8、9位ID
     *
     * @param id
     * @return
     */
    public static Long decodeID(String id) {
        //提前拦截非法ID字符串
        if (id == null || id.length() != 9) {
            return -1L;
        }
        boolean first = id.charAt(0) >= 'A', last = id.charAt(8) >= 'A';
        int ret = -1;
        try {
            if (first || last) {
                //第一位或者最后一位是字母则证明原始ID是8位的
                if (first) {
                    //拿前面的8位去解开就是原始ID
                    ret = decode8ID(id.substring(0, 8));
                } else if (last) {
                    //拿后8位去解开是原始ID
                    ret = decode8ID(id.substring(1));
                }
            } else {
                //否则是9位的原始ID
                if (id.charAt(1) >= 'A') {
                    //如果第二位是字母,解开最后8位并加上第一位
                    ret = decode8ID(id.substring(1)) * 10 + id.charAt(0) - '0';
                } else if (id.charAt(7) >= 'A') {
                    //倒数第二位是字母,解开前8位并加上最后一位
                    ret = decode8ID(id.substring(0, 8)) * 10 + id.charAt(8) - '0';
                }
            }
        } catch (Exception e) {
        }
        //拦截(还原后的ID超出范围的为-1返回,正确的ID会正确解码成功)
        if (ret != -1 && (ret < MIN_ID || ret > MAX_ID)) {
        }
        return new Long(ret);
    }


}

posted @ 2022-09-20 12:07  HumorChen99  阅读(80)  评论(0编辑  收藏  举报  来源