如何生成一个随机的字母数字string?

我一直在寻找一种简单的 Javaalgorithm来生成一个伪随机字母数字string。 在我的情况下,它将被用作一个独特的会话/密钥标识符,“可能”在50万代以上是唯一的(我的需求并不需要任何更复杂的东西)。 理想情况下,我将能够根据我的独特需要指定长度。 例如,生成的长度为12的string可能看起来像"AEYGF7K0DM1X"

algorithm

要生成一个随机string,从可接受符号集合中随机抽取字符,直到string达到所需的长度。

履行

这里有一些相当简单和非常灵活的代码来生成随机标识符。 阅读重要应用笔记中的信息。

 import java.security.SecureRandom; import java.util.Locale; import java.util.Objects; import java.util.Random; 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个字符的标识符创build一个不安全的生成器:

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

为会话标识创build安全的生成器:

 RandomString session = new RandomString(); 

创build一个易于阅读的代码打印生成器。 string比完整的字母数字string长,以补偿使用较less的符号:

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

用作会话标识符

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

长度和安全之间有张力。 较短的标识符更容易被猜出,因为可能性较小。 但是更长的标识符消耗更多的存储和带宽 一组更大的符号有助于解决问题,但如果标识符包含在URL中或手动重新input,可能会导致编码问题。

会话标识符的随机性或熵的基本来源应该来自为密码学devise的随机数发生器。 然而,初始化这些生成器有时可能在计算上是昂贵的或者是很慢的,因此应该努力在可能的时候重新使用它们。

用作对象标识符

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

在没有采取措施使其不可预知的情况下生成的标识符,如果攻击者能够查看和操纵它们,则应该采用其他方法加以保护,就像在大多数Web应用程序中一样。 应该有一个单独的授权系统,保护对象的标识符可以被攻击者没有访问权限的猜测。

考虑到预期的标识符总数,还必须小心使用足够长的标识符来使得碰撞不太可能。 这被称为“生日悖论”。 碰撞概率 p近似为n 2 /(2q x ),其中n是实际生成的标识符的数量, q是字母表中不同符号的数量, x是标识符的长度。 这应该是一个非常小的数字,如2 – 50或更less。

解决这个问题表明,500k个15个字符的标识符之间相互碰撞的机会大约是2 -52 ,这可能比宇宙射线中未被发现的错误等更less。

与UUID进行比较

根据它们的规范,UUID不是不可预知的, 应该被用作会话标识符。

标准格式的UUID需要大量的空间:只有122位熵的36个字符。 (随机select“随机”UUID的所有位都不是随机select的。)随机select的字母数字串仅包含21个字符的熵。

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

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 

如果你很乐意使用Apache类,你可以使用org.apache.commons.text.RandomStringGenerator (commons-text)。

例:

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

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

在一行中:

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

http://mynotes.wordpress.com/2009/07/23/java-generating-random-string/

您可以使用Apache库: RandomStringUtils

 RandomStringUtils.randomAlphanumeric(20).toUpperCase(); 

使用美元应该是简单的:

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

它输出这样的东西:

 DKL1SBH9UJWC JH7P0IT21EA5 5DTI72EO6SFU HQUMJTEBNF7Y 1HCR6SKYWGT7 

这是在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 

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

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

简单。

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

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

“……只有在未来100年内每秒产生10亿个UUID之后,再创造一个副本的可能性就会达到50%左右。”

http://en.wikipedia.org/wiki/Universally_unique_identifier#Random_UUID_probability_of_duplicates

前4位是版本types,2是变体,所以你得到122位的随机数。 所以,如果你想要从最后截断,以减lessUUID的大小。 这是不推荐的,但你仍然有很多的随机性,足以让你的500Klogging容易。

一个简单而简单的解决scheme,但只使用小写和数字:

  r = new util.Random (); Long.toString (Math.abs (r.nextLong ()), 36); 

这个尺寸大约是12位数,以36为底,不能再进一步改进。 当然你可以附加多个实例。

这是很容易实现的,没有任何外部库。

1.encryption伪随机数据生成

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

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

注意: SecureRandom是Java中生成随机字节的最慢但最安全的方式。 不过我build议不要在这里考虑性能,因为它通常对你的应用程序没有真正的影响,除非你必须每秒产生数百万个令牌。

2.可能值的所需空间

接下来,你必须决定你的代币需要的“独特性”。 考虑熵的唯一要点是确保系统能够抵抗暴力攻击:可能值的空间必须非常大,以至于任何攻击者在非滑稽时间只能尝试微不足道的比例。 唯一的标识符,如随机的UUID有122位的熵(即2 ^ 122 = 5.3×10 ^ 36) – 碰撞的机会是“*(…)十亿分之一的机会,103万亿版本4 UUID必须生成2 “。 我们将select128位,因为它恰好适合于16个字节,并且被认为对于基本上每一个都是独一无二的,而且是最极端的用例,您不必考虑重复。 这里是一个简单的熵比较表,包括生日问题的简单分析。

令牌大小的比较

对于简单的要求8或12字节长度可能就足够了,但是有16个字节,你是在“安全的一面”。

基本上就是这样。 最后一件事是考虑编码,所以它可以表示为一个可打印的文本(读,一个String )。

3.二进制文本编码

典型的编码包括:

  • Base64每个字符编码6bit,产生33%的开销。 不幸的是,在JDK中没有标准的实现(在Android中 )。 但是现在有很多图书馆增加了这个。 缺点是, Base64对于例如。 在大多数需要额外编码的文件系统(例如url编码 )中作为文件名。 使用填充编码16个字节的示例: XfJhfv3C0P6ag7y9VQxSbw==

  • Base32每个字符编码5bit,产生40%的开销。 这将使用AZ2-7 ,使其合理的空间效率,而不区分大小写的字母数字。 像Base64一样, JDK中没有标准的实现 。 编码16个字节而没有填充的示例: WUPIL5DQTZGMF4D3NX5L7LNFOY

  • Base16 (hex)每个字符编码4位,每个字节需要2个字符(即16个字节创build一个长度为32的string)。 因此hex的空间效率比Base32less,但是在大多数情况下(url)使用是安全的,因为它只使用0-9AF 编码16字节的示例: 4fa3dd0f57cb3bf331441ed285b27735 。 在这里看到关于转换为hex的讨论。

像Base85和异域Base122的其他编码存在更好/更差的空间效率。 你可以创build自己的编码(基本上这个线程中的大部分答案都可以),但如果你没有特别的要求,我会build议你不要这样做。 请参阅Wikipedia文章中的更多编码scheme。

4.总结和例子

  • 使用SecureRandom
  • 使用16个字节的可能值
  • 根据您的要求进行编码(如果您需要它是字母数字,通常是hexbase32

  • …使用家庭brew编码: 更好的可维护性和可读性,如果他们看到什么标准的编码,而不是奇怪的一次循环创build字符。
  • …使用UUID: 你正在浪费6位的熵,并有详细的string表示

示例:hex令牌生成器

 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); //hex encoding } //generateRandomHexToken(16) -> 2189df7475e96aa3982dbeab266497cd 

示例:工具

如果你想要一个现成的cli工具,你可以使用骰子: https : //github.com/patrickfav/dice

 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++) //12 result = result + alphabet.charAt(r.nextInt(n)); //13 return result; } 

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) .mapToObj((i) -> (char) i) .collect(StringBuilder::new, StringBuilder::append, StringBuilder::append) .toString(); } 
 import java.util.Random; public class passGen{ //Verison 1.0 private 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 String pass = ""; 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(25); pass += dCase.charAt(spot); } else if (rPick == 1) { int spot = r.nextInt (25); pass += uCase.charAt(spot); } else if (rPick == 2) { int spot = r.nextInt (7); pass += sChar.charAt(spot); } else if (rPick == 3){ int spot = r.nextInt (9); pass += intChar.charAt (spot); } } System.out.println ("Generated Pass: " + pass); } } 

所以这是做的只是添加到string的密码和…是啊作品很好检查出来…非常简单。 我写的

使用UUID是不安全的,因为UUID的部分不是随机的。 @erickson的过程非常整洁,但不会创build长度相同的string。 以下片段应该足够了:

 /* * 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'); } } 

为什么selectlength*5 。 我们假设一个长度为1的随机string的简单情况,那么一个随机字符。 要得到一个包含所有数字0-9和字符az的随机字符,我们需要一个介于0到35之间的随机数来获得每个字符的一个。 BigInteger提供了一个构造函数来生成一个随机数,均匀分布在0 to (2^numBits - 1)的范围内。 不幸的是35没有数字可以通过2 ^ numBits – 1来接收。所以我们有两个select:或者用2^5-1=31或者2^6-1=63 。 如果我们select2^6我们会得到很多“不必要的”/“更长的”数字。 所以2^5是更好的select,即使我们松了4个字符(wz)。 现在要生成一定长度的string,我们可以简单地使用2^(length*numBits)-1数字。 最后一个问题,如果我们想要一个具有一定长度的string,随机可以产生一个小数字,所以长度不符合,所以我们必须填充string到它所需的长度前置零。

我发现这个解决scheme生成一个随机的hex编码的字符 提供的unit testing似乎支持我的主要用例。 虽然它比其他一些答案稍微复杂一些。

 /** * 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)); else buffer.append((char) ('A' + (b1 - 10))); if (b2 < 10) buffer.append((char) ('0' + b2)); else buffer.append((char) ('A' + (b2 - 10))); } return (buffer.toString()); } @Test public 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); } } } 
 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.*; 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); } } } 
  1. 根据您的要求更改string字符。

  2. string是不可变的。 这里StringBuilder.append比string连接更有效率。

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

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

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

Here is the one line code by AbacusUtil

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

Random doesn't mean it must be unique. to get unique strings, using:

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

You mention "simple", but just in case anyone else is looking for something that meets more stringent security requirements, you might want to take a look at jpwgen . jpwgen is modeled after pwgen in Unix, and is very configurable.

Here it is a Scala solution:

 (for (i <- 0 until rnd.nextInt(64)) yield { ('0' + rnd.nextInt(64)).asInstanceOf[Char] }) mkString("") 

using apache library it can be done in one line

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

here is doc http://commons.apache.org/lang/api-2.3/org/apache/commons/lang/RandomStringUtils.html

You can use the UUID class with its getLeastSignificantBits() message to get 64bit of Random data, then convert it to a radix 36 number (ie a string consisting of 0-9,AZ):

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

This yields a String up to 13 characters long. We use Math.abs() to make sure there isn't a minus sign sneaking in.

Maybe this is helpful

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

Best Random String Generator Method

 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; } } 
 public static String getRandomString(int length) { String randomStr = UUID.randomUUID().toString(); while(randomStr.length() < length) { randomStr += UUID.randomUUID().toString(); } return randomStr.substring(0, length); } 

Lots of use of StringBuilder above. I guess it's easy, but requires a function call per char, growing an array, etc… If using the stringbuilder, a suggestion is to specify the required capacity of the string ie.,

 new StringBuilder(int capacity); 

Here's a version that doesn't use a StringBuilder or String appending, and no dictionary.

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