如何生成随机字母数字字符串

我一直在寻找一个简单Java算法来生成一个伪随机的字母数字字符串。在我的情况下,它将被用作一个唯一的会话/密钥标识符,在500K+代中“可能”是唯一的(我的需求并不需要任何更复杂的东西)。

理想情况下,我可以根据我的唯一性需求指定长度。例如,生成的长度为12的字符串可能看起来像"AEYGF7K0DM1X"

1610899 次浏览

算法

要生成随机字符串,请将从可接受符号集中随机抽取的字符连接起来,直到字符串达到所需的长度。

实施

这是一些相当简单且非常灵活的代码,用于生成随机标识符。阅读以下信息用于重要的应用说明。

public class RandomString {
/*** Generate a random string.*/public String nextString() {for (int idx = 0; idx < buf.length; ++idx)buf[idx] = symbols[random.nextInt(symbols.length)];return new String(buf);}
public static final String upper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
public static final String lower = upper.toLowerCase(Locale.ROOT);
public static final String digits = "0123456789";
public static final String alphanum = upper + lower + digits;
private final Random random;
private final char[] symbols;
private final char[] buf;
public RandomString(int length, Random random, String symbols) {if (length < 1) throw new IllegalArgumentException();if (symbols.length() < 2) throw new IllegalArgumentException();this.random = Objects.requireNonNull(random);this.symbols = symbols.toCharArray();this.buf = new char[length];}
/*** Create an alphanumeric string generator.*/public RandomString(int length, Random random) {this(length, random, alphanum);}
/*** Create an alphanumeric strings from a secure generator.*/public RandomString(int length) {this(length, new SecureRandom());}
/*** Create session identifiers.*/public RandomString() {this(21);}
}

使用示例

为8个字符标识符创建一个不安全的生成器:

RandomString gen = new RandomString(8, ThreadLocalRandom.current());

为会话标识符创建安全生成器:

RandomString session = new RandomString();

创建一个带有易于阅读的打印代码的生成器。字符串比完整的字母数字字符串长,以弥补使用更少的符号:

String easy = RandomString.digits + "ACEFGHJKLMNPQRUVWXYabcdefhijkprstuvwx";RandomString tickets = new RandomString(23, new SecureRandom(), easy);

用作会话标识符

生成可能是唯一的会话标识符是不够的,或者您可以只使用一个简单的计数器。当使用可预测的标识符时,攻击者劫持会话。

长度和安全性之间存在紧张关系。较短的标识符更容易猜测,因为可能性较小。但较长的标识符消耗更多的存储空间和带宽。较大的符号集有帮助,但如果标识符包含在URL中或手动重新输入,可能会导致编码问题。

会话标识符的随机性或熵的潜在来源应该来自为密码学设计的随机数生成器。然而,初始化这些生成器有时会计算成本高或速度慢,因此应尽可能重用它们。

用作对象标识符

并非每个应用程序都需要安全性。随机分配可能是多个实体在共享空间中生成标识符的有效方式,无需任何协调或分区。协调可能很慢,尤其是在集群或分布式环境中。当实体最终获得的共享太小或太大时,分割空间会导致问题。

如果攻击者能够查看和操纵标识符,就像大多数Web应用程序中发生的那样,在没有采取措施使其不可预测的情况下生成的标识符应该通过其他方式进行保护。应该有一个单独的授权系统来保护其标识符可以被攻击者在没有门禁权限的情况下猜测的对象。

还必须注意使用足够长的标识符,以使在预期的标识符总数下不太可能发生冲突。这被称为“生日悖论”。碰撞的概率,p约为n2/(2qx),其中n是实际生成的标识符的数量,q是字母表中不同符号的数量,x是标识符的长度。这应该是一个非常小的数字,比如2-50或更少。

计算结果表明,500k15个字符标识符之间发生碰撞的可能性约为2-52,这可能比宇宙射线等未检测到的错误更小。

与UUID的比较

根据他们的规范,uuid不是被设计成不可预测的,而不应该被用作会话标识符。

标准格式的UUID占用大量空间:36个字符仅包含122位熵。(并非“随机”UUID的所有位都是随机选择的。)随机选择的字母数字字符串仅包含21个字符。

UUID不灵活;它们有一个标准化的结构和布局。这是它们的主要优点,也是它们的主要弱点。当与外部合作时,UUID提供的标准化可能会有所帮助。纯粹在内部使用,它们可能效率低下。

这是Java:

import static java.lang.Math.round;import static java.lang.Math.random;import static java.lang.Math.pow;import static java.lang.Math.abs;import static java.lang.Math.min;import static org.apache.commons.lang.StringUtils.leftPad
public class RandomAlphaNum {public static String gen(int length) {StringBuffer sb = new StringBuffer();for (int i = length; i > 0; i -= 12) {int n = min(12, abs(i));sb.append(leftPad(Long.toString(round(random() * pow(36, n)), 36), n, '0'));}return sb.toString();}}

这是一个示例运行:

scala> RandomAlphaNum.gen(42)res3: java.lang.String = uja6snx21bswf9t89s00bxssu8g6qlu16ffzqaxxoy

Java提供了一种直接执行此操作的方法。如果您不想要破折号,它们很容易被剥离。只需使用uuid.replace("-", "")

import java.util.UUID;
public class randomStringGenerator {public static void main(String[] args) {System.out.println(generateString());}
public static String generateString() {String uuid = UUID.randomUUID().toString();return "uuid = " + uuid;}}

产出

uuid = 2d7428a6-b58c-4008-8575-f05549f16316

我找到了这个生成随机十六进制编码字符串的解决方案。提供的单元测试似乎符合我的主要用例。虽然,它比提供的其他一些答案稍微复杂一些。

/*** Generate a random hex encoded string token of the specified length** @param length* @return random hex string*/public static synchronized String generateUniqueToken(Integer length){byte random[] = new byte[length];Random randomGenerator = new Random();StringBuffer buffer = new StringBuffer();
randomGenerator.nextBytes(random);
for (int j = 0; j < random.length; j++) {byte b1 = (byte) ((random[j] & 0xf0) >> 4);byte b2 = (byte) (random[j] & 0x0f);if (b1 < 10)buffer.append((char) ('0' + b1));elsebuffer.append((char) ('A' + (b1 - 10)));if (b2 < 10)buffer.append((char) ('0' + b2));elsebuffer.append((char) ('A' + (b2 - 10)));}return (buffer.toString());}
@Testpublic void testGenerateUniqueToken(){Set set = new HashSet();String token = null;int size = 16;
/* Seems like we should be able to generate 500K tokens* without a duplicate*/for (int i=0; i<500000; i++){token = Utility.generateUniqueToken(size);
if (token.length() != size * 2){fail("Incorrect length");} else if (set.contains(token)) {fail("Duplicate token generated");} else{set.add(token);}}}

如果你喜欢使用Apache类,你可以使用#0Apache Commons文本)。

示例:

RandomStringGenerator randomStringGenerator =new RandomStringGenerator.Builder().withinRange('0', 'z').filteredBy(CharacterPredicates.LETTERS, CharacterPredicates.DIGITS).build();randomStringGenerator.generate(12); // toUpperCase() if you want

Apache Commons Lang 3.6开始,RandomStringUtils已弃用。

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();}

我已经开发了一个应用程序来为我的项目开发一个自动生成的字母数字字符串。在这个字符串中,前三个字符是按字母顺序排列的,接下来的七个是整数。

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);}}

在一行:

Long.toHexString(Double.doubleToLongBits(Math.random()));

来源:Java-生成一个随机字符串

使用美元应该很简单:

// "0123456789" + "ABCDE...Z"String validCharacters = $('0', '9').join() + $('A', 'Z').join();
String randomString(int length) {return $(validCharacters).shuffle().slice(length).toString();}
@Testpublic void buildFiveRandomStrings() {for (int i : $(5)) {System.out.println(randomString(12));}}

它输出这样的东西:

DKL1SBH9UJWCJH7P0IT21EA55DTI72EO6SFUHQUMJTEBNF7Y1HCR6SKYWGT7
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);}}}

你提到了“简单”,但如果其他人正在寻找满足更严格安全要求的东西,你可能想看看jpwgen。jpwgen是仿照Unix中的pwgen建模的,并且非常可配置。

import java.util.Date;import java.util.Random;
public class RandomGenerator {
private static Random random = new Random((new Date()).getTime());
public static String generateRandomString(int length) {char[] values = {'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','0','1','2','3','4','5','6','7','8','9'};
String out = "";
for (int i=0;i<length;i++) {int idx=random.nextInt(values.length);out += values[idx];}return out;}}
import java.util.Random;
public class passGen{// Version 1.0private static final String dCase = "abcdefghijklmnopqrstuvwxyz";private static final String uCase = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";private static final String sChar = "!@#$%^&*";private static final String intChar = "0123456789";private static Random r = new Random();private static StringBuilder pass = new StringBuilder();
public static void main (String[] args) {System.out.println ("Generating pass...");while (pass.length () != 16){int rPick = r.nextInt(4);if (rPick == 0){int spot = r.nextInt(26);pass.append(dCase.charAt(spot));} else if (rPick == 1) {int spot = r.nextInt(26);pass.append(uCase.charAt(spot));} else if (rPick == 2) {int spot = r.nextInt(8);pass.append(sChar.charAt(spot));} else {int spot = r.nextInt(10);pass.append(intChar.charAt(spot));}}System.out.println ("Generated Pass: " + pass.toString());}}

这只是将密码添加到字符串中……是的,它运行良好。检查一下……这很简单;我写的。

一个简短而简单的解决方案,但它只使用小写和数字:

Random r = new java.util.Random ();String s = Long.toString (r.nextLong () & Long.MAX_VALUE, 36);

大小大约是以36为基数的12位数字,因此无法进一步改进。当然,您可以附加多个实例。

最佳随机字符串生成器方法

public class RandomStringGenerator{
private static int randomStringLength = 25 ;private static boolean allowSpecialCharacters = true ;private static String specialCharacters = "!@$%*-_+:";private static boolean allowDuplicates = false ;
private static boolean isAlphanum = false;private static boolean isNumeric = false;private static boolean isAlpha = false;private static final String alphabet = "abcdefghijklmnopqrstuvwxyz";private static boolean mixCase = false;private static final String capAlpha = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";private static final String num = "0123456789";
public static String getRandomString() {String returnVal = "";int specialCharactersCount = 0;int maxspecialCharacters = randomStringLength/4;
try {StringBuffer values = buildList();for (int inx = 0; inx < randomStringLength; inx++) {int selChar = (int) (Math.random() * (values.length() - 1));if (allowSpecialCharacters){if (specialCharacters.indexOf("" + values.charAt(selChar)) > -1){specialCharactersCount ++;if (specialCharactersCount > maxspecialCharacters){while (specialCharacters.indexOf("" + values.charAt(selChar)) != -1){selChar = (int) (Math.random() * (values.length() - 1));}}}}returnVal += values.charAt(selChar);if (!allowDuplicates) {values.deleteCharAt(selChar);}}} catch (Exception e) {returnVal = "Error While Processing Values";}return returnVal;}
private static StringBuffer buildList() {StringBuffer list = new StringBuffer(0);if (isNumeric || isAlphanum) {list.append(num);}if (isAlpha || isAlphanum) {list.append(alphabet);if (mixCase) {list.append(capAlpha);}}if (allowSpecialCharacters){list.append(specialCharacters);}int currLen = list.length();String returnVal = "";for (int inx = 0; inx < currLen; inx++) {int selChar = (int) (Math.random() * (list.length() - 1));returnVal += list.charAt(selChar);list.deleteCharAt(selChar);}list = new StringBuffer(returnVal);return list;}
}

您可以为此使用Apache Commons库,Utils实例化

RandomStringUtils.randomAlphanumeric(20).toUpperCase();

这是一个Scala解决方案:

(for (i <- 0 until rnd.nextInt(64)) yield {('0' + rnd.nextInt(64)).asInstanceOf[Char]}) mkString("")
public static String generateSessionKey(int length){String alphabet =new String("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"); // 9
int n = alphabet.length(); // 10
String result = new String();Random r = new Random(); // 11
for (int i=0; i<length; i++) // 12result = result + alphabet.charAt(r.nextInt(n)); //13
return result;}

使用Apache Commons库,可以在一行中完成:

import org.apache.commons.lang.RandomStringUtils;RandomStringUtils.randomAlphanumeric(64);

文档

令人惊讶的是,这里没有人建议它,但是:

import java.util.UUID
UUID.randomUUID().toString();

简单。

这样做的好处是UUID很好,很长,并且保证几乎不可能发生碰撞。

维基百科有一个很好的解释

“只有在接下来的100年里每秒生成10亿个UUID之后,只创建一个副本的概率才会达到50%左右。

前四位是版本类型,两位是变体,所以你得到122位随机。所以如果你想要 to,你可以从末尾截断以减少UUID的大小。这不推荐,但你仍然有大量的随机性,足以让你的500k记录容易。

如果您的密码强制包含数字和字母特殊字符,您可以使用以下代码:

private static final String NUMBERS = "0123456789";private static final String UPPER_ALPHABETS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";private static final String LOWER_ALPHABETS = "abcdefghijklmnopqrstuvwxyz";private static final String SPECIALCHARACTERS = "@#$%&*";private static final int MINLENGTHOFPASSWORD = 8;
public static String getRandomPassword() {StringBuilder password = new StringBuilder();int j = 0;for (int i = 0; i < MINLENGTHOFPASSWORD; i++) {password.append(getRandomPasswordCharacters(j));j++;if (j == 3) {j = 0;}}return password.toString();}
private static String getRandomPasswordCharacters(int pos) {Random randomNum = new Random();StringBuilder randomChar = new StringBuilder();switch (pos) {case 0:randomChar.append(NUMBERS.charAt(randomNum.nextInt(NUMBERS.length() - 1)));break;case 1:randomChar.append(UPPER_ALPHABETS.charAt(randomNum.nextInt(UPPER_ALPHABETS.length() - 1)));break;case 2:randomChar.append(SPECIALCHARACTERS.charAt(randomNum.nextInt(SPECIALCHARACTERS.length() - 1)));break;case 3:randomChar.append(LOWER_ALPHABETS.charAt(randomNum.nextInt(LOWER_ALPHABETS.length() - 1)));break;}return randomChar.toString();}
public static String getRandomString(int length){String randomStr = UUID.randomUUID().toString();while(randomStr.length() < length) {randomStr += UUID.randomUUID().toString();}return randomStr.substring(0, length);}

在之前的答案中有很多使用StringBuilder。我想这很容易,但它需要每个字符调用一个函数,生长一个数组等…

如果使用字符串生成器,建议指定字符串所需的容量,即:

new StringBuilder(int capacity);

这是一个不使用StringBuilder或String附加的版本,也没有字典。

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);}
public static String randomSeriesForThreeCharacter() {Random r = new Random();String value = "";char random_Char ;for(int i=0; i<10; i++){random_Char = (char) (48 + r.nextInt(74));value = value + random_char;}return value;}

您可以创建一个包含所有字母和数字的字符数组,然后您可以从该字符数组中随机选择并创建您自己的字符串密码。

char[] chars = new char[62]; // Sum of letters and numbers
int i = 0;
for(char c = 'a'; c <= 'z'; c++) { // For letterschars[i++] = c;}
for(char c = '0'; c <= '9';c++) { // For numberschars[i++] = c;}
for(char c = 'A'; c <= 'Z';c++) { // For capital letterschars[i++] = c;}
int numberOfCodes = 0;String code = "";while (numberOfCodes < 1) { // Enter how much you want to generate at one timeint numChars = 8; // Enter how many digits you want in your password
for(i = 0; i < numChars; i++) {char c = chars[(int)(Math.random() * chars.length)];code = code + c;}System.out.println("Code is:" + code);}

您可以使用UUID类及其getLeast重大位()消息来获取64位的随机数据,然后将其转换为基数36(即由0-9,A-Z组成的字符串):

Long.toString(Math.abs( UUID.randomUUID().getLeastSignificantBits(), 36));

这将产生一个最多13个字符的字符串。我们使用Math.abs()来确保没有减号潜入。

  1. 根据您的要求更改String个字符。

  2. 字符串是不可变的。这里StringBuilder.append比字符串连接更有效。


public static String getRandomString(int length) {final String characters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJLMNOPQRSTUVWXYZ1234567890!@#$%^&*()_+";StringBuilder result = new StringBuilder();
while(length > 0) {Random rand = new Random();result.append(characters.charAt(rand.nextInt(characters.length())));length--;}return result.toString();}

Java8中的替代方案是:

static final Random random = new Random(); // Or SecureRandomstatic 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();}

另一个解决方案…

public static String generatePassword(int passwordLength) {int asciiFirst = 33;int asciiLast = 126;Integer[] exceptions = { 34, 39, 96 };
List<Integer> exceptionsList = Arrays.asList(exceptions);SecureRandom random = new SecureRandom();StringBuilder builder = new StringBuilder();for (int i=0; i<passwordLength; i++) {int charIndex;
do {charIndex = random.nextInt(asciiLast - asciiFirst + 1) + asciiFirst;}while (exceptionsList.contains(charIndex));
builder.append((char) charIndex);}return builder.toString();}

使用UUID是不安全的,因为UUID的部分根本不是随机的。埃里克森程序非常简洁,但它不会创建相同长度的字符串。以下片段应该就足够了:

/** The random generator used by this class to create random keys.* In a holder class to defer initialization until needed.*/private static class RandomHolder {static final Random random = new SecureRandom();public static String randomKey(int length) {return String.format("%"+length+"s", new BigInteger(length*5/*base 32,2^5*/, random).toString(32)).replace('\u0020', '0');}}

为什么选择length*5?让我们假设一个长度为1的随机字符串的简单情况,即一个随机字符。要获取包含所有数字0-9和字符a-z的随机字符,我们需要一个介于0到35之间的随机数来获取每个字符中的一个。

BigInteger提供了一个构造函数来生成一个均匀分布在0 to (2^numBits - 1)范围内的随机数。不幸的是,35不是一个可以被2^numBits-1接收的数字。

所以我们有两个选择:要么选择2^5-1=31,要么选择2^6-1=63。如果我们选择2^6,我们会得到很多“不必要”/“更长”的数字。因此2^5是更好的选择,即使我们失去了四个字符(w-z)。现在要生成一个一定长度的字符串,我们可以简单地使用2^(length*numBits)-1数字。最后一个问题,如果我们想要一个具有一定长度的字符串,随机可以生成一个小数字,所以长度不满足,所以我们必须将字符串填充到所需长度的前置零。

也许这个有用

package password.generater;
import java.util.Random;
/**** @author dell*/public class PasswordGenerater {
/*** @param args the command line arguments*/public static void main(String[] args) {int length= 11;System.out.println(generatePswd(length));
// TODO code application logic here}static char[] generatePswd(int len){System.out.println("Your Password ");String charsCaps="ABCDEFGHIJKLMNOPQRSTUVWXYZ";String Chars="abcdefghijklmnopqrstuvwxyz";String nums="0123456789";String symbols="!@#$%^&*()_+-=.,/';:?><~*/-+";String passSymbols=charsCaps + Chars + nums +symbols;Random rnd=new Random();char[] password=new char[len];
for(int i=0; i<len;i++){password[i]=passSymbols.charAt(rnd.nextInt(passSymbols.length()));}return password;
}}

以下是算术软件的单行代码:

String.valueOf(CharStream.random('0', 'z').filter(c -> N.isLetterOrDigit(c)).limit(12).toArray())

Random并不意味着它必须是唯一的。要获得唯一的字符串,请使用:

N.uuid() // E.g.: "e812e749-cf4c-4959-8ee1-57829a69a80f". length is 36.N.guid() // E.g.: "0678ce04e18945559ba82ddeccaabfcd". length is 32 without '-'

这在没有任何外部库的情况下很容易实现。

1.密码伪随机数据生成(PRNG)

首先,你需要一个加密的PRNG。Java有#0,通常使用机器上最好的熵源(例如/dev/random)。在这里阅读更多

SecureRandom rnd = new SecureRandom();byte[] token = new byte[byteLength];rnd.nextBytes(token);

注:SecureRandom是生成随机字节Java最慢但最安全的方法。但是,我建议在这里考虑性能,因为它通常对您的应用程序没有实际影响,除非您必须每秒生成数百万个令牌。

2.可能值的必要空间

接下来,你必须决定你的令牌需要有多“独特”。考虑熵的全部和唯一目的是确保系统能够抵御蛮力攻击:可能值的空间必须如此之大,以至于任何攻击者都只能在非可笑的时间1内尝试可忽略不计的值比例。

唯一标识符,例如随机#0,有122位的熵(即,2^122=5.3x10^36)-碰撞的机会是“*(…),因为有十亿分之一的重复机会,103万亿版本4的UUID必须生成2”。我们将选择128位,因为它完全适合16个字节被视为非常足够,因为基本上每个,但最极端的,用例都是唯一的,你不必考虑重复。这是一个简单的熵比较表,包括对生日问题的简单分析。

令牌大小比较

对于简单的需求,8或12字节的长度可能就足够了,但是16字节是“安全的”。

最后一件事是考虑编码,以便它可以表示为可打印文本(读取,String)。

3.二进制到文本编码

典型的编码包括:

  • #0每个字符编码6位,产生33%的开销。幸运的是Java8+android中有标准实现。对于较旧的Java您可以使用众多第三方库中的任何一个。如果您希望您的令牌是URL安全的,请使用URL安全的版本的RFC4648(大多数实现通常支持)。使用填充编码16字节的示例:XfJhfv3C0P6ag7y9VQxSbw==

  • #0每个字符编码5位,产生40%的开销。这将使用A-Z2-7,使其合理地节省空间,同时不区分大小写的字母数字。没有任何JDK中的标准实现。编码16个字节而不填充的示例:WUPIL5DQTZGMF4D3NX5L7LNFOY

  • #0(十六进制)每个字符编码四位,每字节需要两个字符(即,16字节创建一个长度为32的字符串)。因此,十六进制的空间效率低于Base32,但在大多数情况下(URL)使用是安全的,因为它只使用0-9AF。编码16字节的示例:4fa3dd0f57cb3bf331441ed285b27735在此处查看有关转换为十六进制的Stack Overflow讨论

Base85和异国情调的Base122这样的附加编码存在更好/更差的空间效率。你可以创建自己的编码(基本上这个线程中的大多数答案都这样做),但如果你没有非常具体的要求,我会建议不要这样做。参见维基百科文章中的更多编码方案

4.总结与示例

  • 使用#0
  • 使用至少16字节(2^128)的可能值
  • 根据您的要求进行编码(如果您需要字母数字,通常为hexbase32

不要

  • …使用您的自制编码:如果其他人看到您使用的标准编码而不是奇怪的,则可以更好地维护和阅读循环一次创建字符。
  • …使用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

示例:JavaCLI工具

如果您想要一个即用型CLI工具,您可以使用骰子

示例:相关问题-保护您当前的ID

如果您已经有一个可以使用的id(例如,您的实体中的合成long),但不想发布内部值,您可以使用此库对其进行加密和混淆:https://github.com/patrickfav/id-mask

IdMask<Long> idMask = IdMasks.forLongIds(Config.builder(key).build());String maskedId = idMask.mask(id);// Example: NPSBolhMyabUBdTyanrbqT8long originalId = idMask.unmask(maskedId);

此外,您可以通过ASCII表中的数据生成任何小写或大写字母,甚至特殊字符。例如,生成从A(DEC 65)到Z(DEC 90)的大写字母:

String generateRandomStr(int min, int max, int size) {String result = "";for (int i = 0; i < size; i++) {result += String.valueOf((char)(new Random().nextInt((max - min) + 1) + min));}return result;}

generateRandomStr(65, 90, 100));生成输出:

TVLPFQJCYFXQDCQSLKUKKILKKHAUFYEXLUQFHDWNMRBIRRRWNXNNZQTINZPCTKLHGHVYWRKEOYNSOFPZBGEECFMCOKWHLHCEWLDZ

我真的不喜欢这些关于“简单”解决方案的答案:S

我会选择一个简单的;),纯Java,一行(熵基于随机字符串长度和给定字符集):

public String randomString(int length, String characterSet) {return IntStream.range(0, length).map(i -> new SecureRandom().nextInt(characterSet.length())).mapToObj(randomInt -> characterSet.substring(randomInt, randomInt + 1)).collect(Collectors.joining());}
@Testpublic void buildFiveRandomStrings() {for (int q = 0; q < 5; q++) {System.out.println(randomString(10, "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")); // The character set can basically be anything}}

或者(一种更可读的旧方式)

public String randomString(int length, String characterSet) {StringBuilder sb = new StringBuilder(); // Consider using StringBuffer if neededfor (int i = 0; i < length; i++) {int randomInt = new SecureRandom().nextInt(characterSet.length());sb.append(characterSet.substring(randomInt, randomInt + 1));}return sb.toString();}
@Testpublic void buildFiveRandomStrings() {for (int q = 0; q < 5; q++) {System.out.println(randomString(10, "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")); // The character set can basically be anything}}

但另一方面,你也可以使用UUID,它具有相当好的熵:

UUID.randomUUID().toString().replace("-", "")

这是一个简单的单行代码,使用UUID作为字符库,并能够指定(几乎)任何长度。(是的,我知道以前有人建议使用UUID。)

public static String randString(int length) {return UUID.randomUUID().toString().replace("-", "").substring(0, Math.min(length, 32)) + (length > 32 ? randString(length - 32) : "");}
public static String getRandomString(int length) {char[] chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRST".toCharArray();
StringBuilder sb = new StringBuilder();Random random = new Random();for (int i = 0; i < length; i++) {char c = chars[random.nextInt(chars.length)];sb.append(c);}String randomStr = sb.toString();
return randomStr;}

我认为这是最小的解决方案,或者几乎是最小的解决方案之一:

 public String generateRandomString(int length) {String randomString = "";
final char[] chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz01234567890".toCharArray();final Random random = new Random();for (int i = 0; i < length; i++) {randomString = randomString + chars[random.nextInt(chars.length)];}
return randomString;}

代码运行良好。如果您使用此方法,我建议您使用超过10个字符。碰撞发生在5个字符/30362次迭代时。这花了9秒。

这是一个基于流的Java8解决方案。

    public String generateString(String alphabet, int length) {return generateString(alphabet, length, new SecureRandom()::nextInt);}
// nextInt = bound -> n in [0, bound)public String generateString(String source, int length, IntFunction<Integer> nextInt) {StringBuilder sb = new StringBuilder();IntStream.generate(source::length).boxed().limit(length).map(nextInt::apply).map(source::charAt).forEach(sb::append);
return sb.toString();}

用它像

String alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";int length = 12;String generated = generateString(alphabet, length);System.out.println(generated);

函数nextInt应该接受intbound并返回0bound - 1之间的随机数。

高效且短。

/*** 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) {intstart = ' ',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--;}elsecount++;}return builder.toString();}}
public static String RandomAlphanum(int length){String charstring = "abcdefghijklmnopqrstuvwxyz0123456789";String randalphanum = "";double randroll;String randchar;for (double i = 0; i < length; i++){randroll = Math.random();randchar = "";for (int j = 1; j <= 35; j++){if (randroll <= (1.0 / 36.0 * j)){randchar = Character.toString(charstring.charAt(j - 1));break;}}randalphanum += randchar;}return randalphanum;}

我使用Math.random()使用了一个非常原始的算法。为了增加随机性,您可以直接实现util.Date类。尽管如此,它还是有效的。

我正在使用Apache Commons中的库来生成一个字母数字字符串:

import org.apache.commons.lang3.RandomStringUtils;
String keyLength = 20;RandomStringUtils.randomAlphanumeric(keylength);

这是快速和简单!

我正在使用Java8的一个非常简单的解决方案。只需根据您的需求自定义它。

...import java.security.SecureRandom;...
//Generate a random String of length between 10 to 20.//Length is also randomly generated here.SecureRandom random = new SecureRandom();
String sampleSet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_";
int stringLength = random.ints(1, 10, 21).mapToObj(x -> x).reduce((a, b) -> a).get();
String randomString = random.ints(stringLength, 0, sampleSet.length() - 1).mapToObj(x -> sampleSet.charAt(x)).collect(Collector.of(StringBuilder::new, StringBuilder::append,StringBuilder::append, StringBuilder::toString));

我们可以使用它来生成一个字母数字随机字符串,如下所示(返回的字符串将强制包含一些非数字字符以及一些数字字符):

public String generateRandomString() {            
String sampleSet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_";String sampleSetNumeric = "0123456789";    
String randomString = getRandomString(sampleSet, 10, 21);String randomStringNumeric = getRandomString(sampleSetNumeric, 10, 21);    
randomString = randomString + randomStringNumeric;    
//Convert String to List<Character>List<Character> list = randomString.chars().mapToObj(x -> (char)x).collect(Collectors.toList());    
Collections.shuffle(list);    
//This is needed to force a non-numeric character as the first String//Skip this for() if you don't need this logic
for(;;) {if(Character.isDigit(list.get(0))) Collections.shuffle(list);else break;}    
//Convert List<Character> to StringrandomString = list.stream().map(String::valueOf).collect(Collectors.joining());    
return randomString;    
}
//Generate a random number between the lower bound (inclusive) and upper bound (exclusive)private int getRandomLength(int min, int max) {SecureRandom random = new SecureRandom();return random.ints(1, min, max).mapToObj(x -> x).reduce((a, b) -> a).get();}
//Generate a random String from the given sample string, having a random length between the lower bound (inclusive) and upper bound (exclusive)private String getRandomString(String sampleSet, int min, int max) {SecureRandom random = new SecureRandom();return random.ints(getRandomLength(min, max), 0, sampleSet.length() - 1).mapToObj(x -> sampleSet.charAt(x)).collect(Collector.of(StringBuilder::new, StringBuilder::append,StringBuilder::append, StringBuilder::toString));}
public class Utils {private final Random RANDOM = new SecureRandom();private final String ALPHABET = "0123456789QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm";
private String generateRandomString(int length) {StringBuffer buffer = new StringBuffer(length);for (int i = 0; i < length; i++) {buffer.append(ALPHABET.charAt(RANDOM.nextInt(ALPHABET.length())));}return new String(buffer);}}

给定一些字符(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));}}

您可以在没有外部库的情况下在一行中完成此操作。

int length = 12;String randomString = new Random().ints(48, 122).filter(i -> (i < 58 || i > 64) && (i < 91 || i > 96)).limit(length).collect(StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append).toString();System.out.print(randomString);

我已经将长度分离成一个参数,并添加了一行来打印结果。

这段代码创建了一个以字母数字ascii范围为界的随机整数流。然后它过滤掉一些符号,因为字母数字范围不是连续的。然后它限制长度并将结果收集到一个字符串中。

因为这种方法丢弃了它生成的大约20%的数字/字符(因为它们是符号),所以对性能的影响很小。

我不觉得它特别可读性,但我不认为其他人在一行中提出了原生Java解决方案。