很多场景需要将数据库记录主键发送到前端,对于递增或趋势递增的主键很容易被恶意用户推测出来,因此需要一种可逆性的算法能够将数值型与字符串进行互换。
方案一(数组)、
1.随机生成一个种子([1-9]内的正整数)
int seed = new Random().nextInt(10); // 0要排除掉
2.随意一个十进制的数值,每一位都是有[0-9]组成的,所以[0-9] * seed = [0-81],因此我们需要至少82个不重复的字符
'u','4','+','1','2','I','B','U','x','z','@','c','M','G','f','<','b','(','O','T','H','=','C','D','$','&','p','d','m','o','i','n','a','Z','_','s','3','^','*','P','8','%','/','l','F','R','r','A','9','y','v','.','#','N','g','e','>',')','t','!','5','7','`','?','h','V','S','Q','0','X','K','j','W','J','q','w','|','E','6','k','L','Y'
3.以数值【52337610997641216】举例说明,假设seed取值为3
数值–>字符串
5 | 2 | 3 | 3 | 7 | 6 | 1 | 0 | 9 | 9 | 7 | 6 | 4 | 1 | 2 | 1 | 6 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
5*seed | 2*seed | 3*seed | 3*seed | 7*seed | 6*seed | 1*seed | 0*seed | 9*seed | 9*seed | 7*seed | 6*seed | 4*seed | 1*seed | 2*seed | 1*seed | 6*seed |
15 | 6 | 9 | 9 | 21 | 18 | 3 | 0 | 27 | 27 | 21 | 18 | 12 | 3 | 6 | 3 | 18 |
< | B | z | z | = | O | 1 | u | d | d | = | O | M | 1 | B | 1 | O |
最终得到字符串:
字符串–>数值
< | B | z | z | = | O | 1 | u | d | d | = | O | M | 1 | B | 1 | O |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
15 | 6 | 9 | 9 | 21 | 18 | 3 | 0 | 27 | 27 | 21 | 18 | 12 | 3 | 6 | 3 | 18 |
15/seed | 6/seed | 9/seed | 9/seed | 21/seed | 18/seed | 3/seed | 0/seed | 27/seed | 27/seed | 21/seed | 18/seed | 12/seed | 3/seed | 6/seed | 3/seed | 18/seed |
5 | 2 | 3 | 3 | 7 | 6 | 1 | 0 | 9 | 9 | 7 | 6 | 4 | 1 | 2 | 1 | 6 |
最终还原数值:52337610997641216
优点:算法简单
缺点:1.一次转换中,相同的数值对应的字符相同,容易让恶意用户推测出算法;2.种子只有9个取值,因此一个数值最多只有9种组合方式,第十次一定会出现重复。
一定要保证随意两个数相乘的最大值要小于等于不重复的字符个数。因为如果最大值大于字符个数,就会出现两个数对应同一个字符,在字符串反转为数值就可能出现错误(当然可以增加标识为,但是会导致字符串边长切算法更复杂,而且我们也没有这么多不重复的英文字符可用)
方案二(环路)、
方案二主要针对方案一的缺点进行改进。
1.还是用方案一中的82个字符组合,只不过在方案一是数组格式,方案二我们改进成环,将82个字符收尾相连组成一个环路。
2.生成种子
由于82个字符都不重复,[0-81]范围内的正整数都可以作为种子
int seed = new Random().nextInt(82);
3.以数值【52337610997641216】举例说明,假设seed取值为45
n = 1 --> offset1 = seed + number(1);
n > 1 --> offset(n) = offset(n-1) + number(n)
数值–>字符串
5 | 2 | 3 | 3 | 7 | 6 | 1 | 0 | 9 | 9 | 7 | 6 | 4 | 1 | 2 | 1 | 6 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
(45+5)%82 | (50+2)%82 | (52+3)%82 | (55+3)%82 | (58+7)%82 | (65+6)%82 | (71+1)%82 | (72+0)%82 | (72+9)%82 | (81+9)%82 | (90+7)%82 | (97+6)%82 | (103+4)%82 | (107+1)%82 | (108+2)%82 | (110+1)%82 | (111+6)%82 |
50 | 52 | 55 | 58 | 65 | 71 | 72 | 72 | 81 | 8 | 15 | 21 | 25 | 26 | 28 | 29 | 35 |
v | # | e | t | V | j | W | W | Y | x | < | = | & | p | m | o | s |
最终得到字符串:v#etVjWWYx<=&pmos
字符串–>数值
n = 1 --> val(1) = index(1) - seed;
n > 1 --> val(n) = index(n) - index(n-1);
v | # | e | t | V | j | W | W | Y | x | < | = | & | p | m | o | s |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
50 | 52 | 55 | 58 | 65 | 71 | 72 | 72 | 81 | 8 | 15 | 21 | 25 | 26 | 28 | 29 | 35 |
50-45 | 52-50 | 55-52 | 58-55 | 65-58 | 71-65 | 72-71 | 72-72 | 81-72 | 90-81 | 15-8 | 21-15 | 25-21 | 26-25 | 28-26 | 29-28 | 35-29 |
5 | 2 | 3 | 3 | 7 | 6 | 1 | 0 | 9 | 9 | 7 | 6 | 4 | 1 | 2 | 1 | 6 |
最终还原数值:52337610997641216
优点:改进了方案一中的两个缺点
缺点:如果数组中有0,则相邻两位字符相同
方案三(环路+m)、
方案三主要针对方案二的缺点进行改进。假设m=1
n = 1 --> offset1 = seed + number(1) + m;
n > 1 --> offset(n) = offset(n-1) + number(n) + m
数值–>字符串
5 | 2 | 3 | 3 | 7 | 6 | 1 | 0 | 9 | 9 | 7 | 6 | 4 | 1 | 2 | 1 | 6 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
(45+5)%82+1 | (51+2)%82+1 | (54+3)%82+1 | (58+3)%82+1 | (62+7)%82+1 | (70+6)%82+1 | (77+1)%82+1 | (79+0)%82+1 | (80+9)%82+1 | (90+9)%82+1 | (100+7)%82+1 | (108+6)%82+1 | (115+4)%82+1 | (120+1)%82+1 | (122+2)%82+1 | (125+1)%82+1 | (127+6)%82+1 |
51 | 54 | 58 | 62 | 70 | 77 | 79 | 80 | 8 | 18 | 26 | 33 | 38 | 40 | 43 | 45 | 52 |
. | g | t | ` | K | E | k | L | x | O | p | Z | * | 8 | l | R | # |
最终得到字符串:v#etVjWWYx<=&pmos
字符串–>数值
n = 1 --> val(1) = (index(1) - 1) - seed;
n > 1 --> val(n) = (index(n) - 1) - index(n-1);
. | g | t | ` | K | E | k | L | x | O | p | Z | * | 8 | l | R | # |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
51 | 54 | 58 | 62 | 70 | 77 | 79 | 80 | 8 | 18 | 26 | 33 | 38 | 40 | 43 | 45 | 52 |
(51-1)-45 | (54-1)-51 | (58-1)-54 | (62-1)-58 | (70-1)-62 | (77-1)-70 | (79-1)-77 | (80-1)-79 | (8+82-1)-80 | (18-1)-8) | (26-1)-18 | (33-1)-26 | (38-1)-33 | (40-1)-38 | (43-1)-40 | (45-1)-43 | (52-1)-45 |
5 | 2 | 3 | 3 | 7 | 6 | 1 | 0 | 9 | 9 | 7 | 6 | 4 | 1 | 2 | 1 | 6 |
最终还原数值:52337610997641216
优点:改进了方案而中的缺点
方案四(checksum)、
以上三个方案都能一定程度上防止恶意用户所以输入字符串,但是可能会出现恶意用户输入的字符串正好能转换为一个数值。因此需要增加一个冗余校验,考虑到复杂度和冗余字符串长度,这里采用类似checksum的冗余校验方案。
number % 82生成随机seed,然后将seed对应下标下的字符加到最终字符串中。然后在字符串转换为数值后,将数值整除82,再获取相应下标的字符串,最终进行比对。