我一直在寻找一种简单的Java算法来生成伪随机字母数字字符串。在我的情况下,它将被用作唯一的会话/密钥标识符,“很可能”在超过50万代的时间内是唯一的(我的需求实际上不需要更复杂的东西)。
理想情况下,我可以根据我的独特性需求指定长度。例如,生成的长度为12的字符串可能看起来像“AEYGF7K0DM1X”。
我一直在寻找一种简单的Java算法来生成伪随机字母数字字符串。在我的情况下,它将被用作唯一的会话/密钥标识符,“很可能”在超过50万代的时间内是唯一的(我的需求实际上不需要更复杂的东西)。
理想情况下,我可以根据我的独特性需求指定长度。例如,生成的长度为12的字符串可能看起来像“AEYGF7K0DM1X”。
当前回答
这在没有任何外部库的情况下很容易实现。
1.密码伪随机数据生成(PRNG)
首先,您需要加密PRNG。Java具有SecureRandom,通常使用机器上最好的熵源(例如/dev/random)。在这里阅读更多信息。
SecureRandom rnd = new SecureRandom();
byte[] token = new byte[byteLength];
rnd.nextBytes(token);
注意:SecureRandom是Java中生成随机字节的最慢但最安全的方法。但是,我建议不要在这里考虑性能,因为它通常不会对应用程序产生实际影响,除非您必须每秒生成数百万个令牌。
2.可能值的所需空间
接下来,你必须决定你的令牌需要“多么独特”。考虑熵的唯一目的是确保系统能够抵御暴力攻击:可能值的空间必须如此之大,以至于任何攻击者只能在非荒谬的时间1内尝试微不足道的一部分值。
唯一标识符(如随机UUID)具有122位熵(即,2^122=5.3x10^36)-冲突的可能性为“*(…),要有十亿分之一的重复机会,必须生成103万亿版本4 UUID 2”。我们将选择128位,因为它正好适合16个字节,而且对于基本上每一个但最极端的用例来说,它都是唯一的,而且您不必考虑重复。这是一个简单的熵比较表,包括生日问题的简单分析。
对于简单的需求,8或12字节的长度可能就足够了,但对于16字节,您处于“安全侧”。
基本上就是这样。最后一件事是考虑编码,以便将其表示为可打印文本(读,字符串)。
3.二进制到文本编码
典型编码包括:
Base64每个字符编码6位,产生33%的开销。幸运的是,Java 8+和Android中有标准实现。对于较旧的Java,您可以使用众多第三方库中的任何一个。如果您希望令牌是URL安全的,请使用RFC4648的URL安全版本(大多数实现通常支持该版本)。使用填充编码16个字节的示例:XfJhfv3C0P6ag7y9VQxSbw==Base32每个字符编码5位,产生40%的开销。这将使用A-Z和2-7,使其具有合理的空间效率,同时不区分大小写。JDK中没有任何标准实现。无填充编码16字节的示例:WUPIL5DQZGMF4D3NX5L7LNFOYBase16(十六进制)每个字符编码四位,每个字节需要两个字符(即,16字节创建长度为32的字符串)。因此,十六进制的空间效率低于Base32,但在大多数情况下(URL)使用它是安全的,因为它只使用0-9和A到F。示例编码16个字节:4fadd0f57cb3bf331441ed285b27735。请参阅此处有关转换为十六进制的堆栈溢出讨论。
诸如Base85和奇异Base122之类的附加编码具有更好/更差的空间效率。您可以创建自己的编码(本主题中的大多数答案都是这样做的),但如果您没有非常具体的要求,我建议您不要这样做。请参阅维基百科文章中的更多编码方案。
4.总结和示例
使用SecureRandom至少使用16字节(2^128)的可能值根据您的要求进行编码(如果需要字母数字,通常为十六进制或32进制)
不要
……使用您自己编写的编码:如果其他人看到您使用的标准编码,而不是每次创建字符时使用的奇怪循环,则可以更好地维护和阅读。…使用UUID:它不保证随机性;您浪费了6位熵,并且有一个冗长的字符串表示
示例:十六进制令牌生成器
public static String generateRandomHexToken(int byteLength) {
SecureRandom secureRandom = new SecureRandom();
byte[] token = new byte[byteLength];
secureRandom.nextBytes(token);
return new BigInteger(1, token).toString(16); // Hexadecimal encoding
}
//generateRandomHexToken(16) -> 2189df7475e96aa3982dbeab266497cd
示例:Base64令牌生成器(URL安全)
public static String generateRandomBase64Token(int byteLength) {
SecureRandom secureRandom = new SecureRandom();
byte[] token = new byte[byteLength];
secureRandom.nextBytes(token);
return Base64.getUrlEncoder().withoutPadding().encodeToString(token); //base64 encoding
}
//generateRandomBase64Token(16) -> EEcCCAYuUcQk7IuzdaPzrg
示例:Java CLI工具
如果您想要现成的CLI工具,可以使用dice:
示例:相关问题-保护当前ID
如果您已经有一个可以使用的id(例如,实体中的合成long),但不想发布内部值,则可以使用此库对其进行加密和模糊处理:https://github.com/patrickfav/id-mask
IdMask<Long> idMask = IdMasks.forLongIds(Config.builder(key).build());
String maskedId = idMask.mask(id);
// Example: NPSBolhMyabUBdTyanrbqT8
long originalId = idMask.unmask(maskedId);
其他回答
这里是Scala解决方案:
(for (i <- 0 until rnd.nextInt(64)) yield {
('0' + rnd.nextInt(64)).asInstanceOf[Char]
}) mkString("")
在一行中:
Long.toHexString(Double.doubleToLongBits(Math.random()));
来源:Java-生成随机字符串
在前面的答案中有很多StringBuilder的用法。我想这很简单,但它需要每个字符调用一个函数,增加一个数组,等等。。。
如果使用字符串生成器,建议指定字符串所需的容量,即。,
new StringBuilder(int capacity);
这是一个不使用StringBuilder或字符串附加的版本,也没有字典。
public static String randomString(int length)
{
SecureRandom random = new SecureRandom();
char[] chars = new char[length];
for(int i=0; i<chars.length; i++)
{
int v = random.nextInt(10 + 26 + 26);
char c;
if (v < 10)
{
c = (char)('0' + v);
}
else if (v < 36)
{
c = (char)('a' - 10 + v);
}
else
{
c = (char)('A' - 36 + v);
}
chars[i] = c;
}
return new String(chars);
}
import java.util.*;
import javax.swing.*;
public class alphanumeric {
public static void main(String args[]) {
String nval, lenval;
int n, len;
nval = JOptionPane.showInputDialog("Enter number of codes you require: ");
n = Integer.parseInt(nval);
lenval = JOptionPane.showInputDialog("Enter code length you require: ");
len = Integer.parseInt(lenval);
find(n, len);
}
public static void find(int n, int length) {
String str1 = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
StringBuilder sb = new StringBuilder(length);
Random r = new Random();
System.out.println("\n\t Unique codes are \n\n");
for(int i=0; i<n; i++) {
for(int j=0; j<length; j++) {
sb.append(str1.charAt(r.nextInt(str1.length())));
}
System.out.println(" " + sb.toString());
sb.delete(0, length);
}
}
}
我开发了一个应用程序,为我的项目开发一个自动生成的字母数字字符串。在这个字符串中,前三个字符是字母,后七个字符是整数。
public class AlphaNumericGenerator {
public static void main(String[] args) {
java.util.Random r = new java.util.Random();
int i = 1, n = 0;
char c;
String str = "";
for (int t = 0; t < 3; t++) {
while (true) {
i = r.nextInt(10);
if (i > 5 && i < 10) {
if (i == 9) {
i = 90;
n = 90;
break;
}
if (i != 90) {
n = i * 10 + r.nextInt(10);
while (n < 65) {
n = i * 10 + r.nextInt(10);
}
}
break;
}
}
c = (char)n;
str = String.valueOf(c) + str;
}
while(true){
i = r.nextInt(10000000);
if(i > 999999)
break;
}
str = str + i;
System.out.println(str);
}
}