我一直在寻找一种简单的Java算法来生成伪随机字母数字字符串。在我的情况下,它将被用作唯一的会话/密钥标识符,“很可能”在超过50万代的时间内是唯一的(我的需求实际上不需要更复杂的东西)。
理想情况下,我可以根据我的独特性需求指定长度。例如,生成的长度为12的字符串可能看起来像“AEYGF7K0DM1X”。
我一直在寻找一种简单的Java算法来生成伪随机字母数字字符串。在我的情况下,它将被用作唯一的会话/密钥标识符,“很可能”在超过50万代的时间内是唯一的(我的需求实际上不需要更复杂的东西)。
理想情况下,我可以根据我的独特性需求指定长度。例如,生成的长度为12的字符串可能看起来像“AEYGF7K0DM1X”。
当前回答
一个简单的解决方案,但它只使用小写和数字:
Random r = new java.util.Random ();
String s = Long.toString (r.nextLong () & Long.MAX_VALUE, 36);
大小约为12位数,以36为基数,这样就无法进一步改进。当然,您可以附加多个实例。
其他回答
高效而简短。
/**
* Utility class for generating random Strings.
*/
public interface RandomUtil {
int DEF_COUNT = 20;
Random RANDOM = new SecureRandom();
/**
* Generate a password.
*
* @return the generated password
*/
static String generatePassword() {
return generate(true, true);
}
/**
* Generate an activation key.
*
* @return the generated activation key
*/
static String generateActivationKey() {
return generate(false, true);
}
/**
* Generate a reset key.
*
* @return the generated reset key
*/
static String generateResetKey() {
return generate(false, true);
}
static String generate(boolean letters, boolean numbers) {
int
start = ' ',
end = 'z' + 1,
count = DEF_COUNT,
gap = end - start;
StringBuilder builder = new StringBuilder(count);
while (count-- != 0) {
int codePoint = RANDOM.nextInt(gap) + start;
switch (getType(codePoint)) {
case UNASSIGNED:
case PRIVATE_USE:
case SURROGATE:
count++;
continue;
}
int numberOfChars = charCount(codePoint);
if (count == 0 && numberOfChars > 1) {
count++;
continue;
}
if (letters && isLetter(codePoint)
|| numbers && isDigit(codePoint)
|| !letters && !numbers) {
builder.appendCodePoint(codePoint);
if (numberOfChars == 2)
count--;
}
else
count++;
}
return builder.toString();
}
}
Java 8中的另一种选择是:
static final Random random = new Random(); // Or SecureRandom
static final int startChar = (int) '!';
static final int endChar = (int) '~';
static String randomString(final int maxLength) {
final int length = random.nextInt(maxLength + 1);
return random.ints(length, startChar, endChar + 1)
.collect(StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append)
.toString();
}
给定一些字符(AllCharacters),您可以随机选择字符串中的一个字符。然后使用for循环重复获取随机字符。
public class MyProgram {
static String getRandomString(int size) {
String AllCharacters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
StringBuilder sb = new StringBuilder(size);
int length = AllCharacters.length();
for (int i = 0; i < size; i++) {
sb.append(AllCharacters.charAt((int)(length * Math.random())));
}
return sb.toString();
}
public static void main(String[] args) {
System.out.println(MyProgram.getRandomString(30));
}
}
在沙盒上试试另请参阅其他语言实现随机字符串生成器
static final String AB = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
static SecureRandom rnd = new SecureRandom();
String randomString(int len){
StringBuilder sb = new StringBuilder(len);
for(int i = 0; i < len; i++)
sb.append(AB.charAt(rnd.nextInt(AB.length())));
return sb.toString();
}
这在没有任何外部库的情况下很容易实现。
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);