我一直在寻找一种简单的 Java算法来生成伪随机的字母数字字符串。 在我的情况下,它将用作唯一的会话/密钥标识符,在“超过500K+
世代中“可能”是唯一的(我的需求实际上不需要任何更复杂的东西)。
理想情况下,我可以根据自己的独特性要求指定长度。 例如,生成的长度为12的字符串可能看起来像"AEYGF7K0DM1X"
。
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);
}
}
因此,这只是将密码添加到字符串中,是的,很好地将其签出……非常简单。 我写的
一种简短的解决方案,但仅使用小写和数字:
Random r = new java.util.Random ();
String s = Long.toString (r.nextLong () & Long.MAX_VALUE, 36);
大小以36为底数大约为12位,因此无法进一步改善。 当然,您可以附加多个实例。
您可以为此使用Apache库: RandomStringUtils
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
使用apache库可以在一行中完成
import org.apache.commons.lang.RandomStringUtils;
RandomStringUtils.randomAlphanumeric(64);
这是文档http://commons.apache.org/lang/api-2.3/org/apache/commons/lang/RandomStringUtils.html
令人惊讶的是,这里没有人建议这样做,但是:
import java.util.UUID
UUID.randomUUID().toString();
简单。
这样做的好处是,UUID很好且很长,并且保证几乎不可能发生碰撞。
维基百科对此有很好的解释:
“ ...仅在接下来的100年中每秒生成10亿个UUID之后,仅创建一个副本的可能性就约为50%。”
http://zh.wikipedia.org/wiki/Universally_unique_identifier#Random_UUID_probability_of_duplicates
前4位是版本类型,而第2位是版本,因此您可以获取122位随机数。 因此,如果您愿意 ,可以从末尾截断以减小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);
}
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;
}
一行:
Long.toHexString(Double.doubleToLongBits(Math.random()));
http://mynotes.wordpress.com/2009/07/23/java-generating-random-string/
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();
}
您可以将UUID类与其getLeastSignificantBits()消息一起使用,以获取64位的Random数据,然后将其转换为基数为36的数字(即,由0-9,AZ组成的字符串):
Long.toString(Math.abs( UUID.randomUUID().getLeastSignificantBits(), 36));
这将产生一个最长为13个字符的字符串。 我们使用Math.abs()来确保没有潜入的减号。
根据您的要求更改字符串字符。
字符串是不可变的。 在这里, 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();
}
使用Dollar应该很简单,因为:
// "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 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();
}
使用UUID是不安全的,因为UUID的某些部分根本不是随机的。 @erickson的过程非常简洁,但是不会创建相同长度的字符串。 以下代码段就足够了:
/*
* 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和字符az的随机字符,我们需要0到35之间的随机数才能获得每个字符之一。 BigInteger
提供了一个构造函数来生成一个随机数,该随机数均匀地分布在0 to (2^numBits - 1)
的范围内。 不幸的是35不是2 ^ numBits-1可以接收的数字。因此,我们有两个选择:要么2^5-1=31
要么2^6-1=63
。 如果我们选择2^6
,则会得到很多“不必要的” /“更长的”数字。 因此,即使我们松开4个字符(wz), 2^5
是更好的选择。 现在要生成一定长度的字符串,我们可以简单地使用2^(length*numBits)-1
数字。 最后一个问题是,如果我们想要一个具有一定长度的字符串,则random可能会生成一个较小的数字,因此长度无法满足,因此我们必须将该字符串填充到所需的长度(以零开头)。
这是AbacusUtil的一行代码
String.valueOf(CharStream.random('0', 'z').filter(c -> N.isLetterOrDigit(c)).limit(12).toArray())
随机并不意味着它必须是唯一的。 要获取唯一的字符串,请使用:
N.uuid() // e.g.: "e812e749-cf4c-4959-8ee1-57829a69a80f". length is 36.
N.guid() // e.g.: "0678ce04e18945559ba82ddeccaabfcd". length is 32 without '-'
要生成随机字符串,请连接从可接受的符号集中随机抽取的字符,直到字符串达到所需的长度为止。
这是一些相当简单且非常灵活的代码,用于生成随机标识符。 阅读以下信息以获取重要的应用笔记。
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个字符的标识符创建不安全的生成器:
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大约为n 2 /(2q x ),其中n是实际生成的标识符的数量, q是字母中不同符号的数量,并且x是标识符的长度。 这应该是一个很小的数字,例如2 -50或更少。
得出的结论表明,在500k个15个字符的标识符之间发生碰撞的机会约为2到52 ,这比宇宙射线等未检测到的错误的可能性要小。
根据其规范,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));
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);
}
}
}
如果您愿意使用Apache类,则可以使用org.apache.commons.text.RandomStringGenerator
(公共文本)。
例:
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
。
无需任何外部库即可轻松实现。
首先,您需要加密的PRNG。 Java为此提供了SecureRandom
,通常使用机器上最好的熵源(例如/dev/random
)。 在这里阅读更多。
SecureRandom rnd = new SecureRandom();
byte[] token = new byte[byteLength];
rnd.nextBytes(token);
注意: SecureRandom
是Java中生成随机字节的最慢但最安全的方法。 但是,我建议不要在这里考虑性能,因为它通常不会对您的应用程序产生实际影响,除非您必须每秒生成数百万个令牌。
接下来,您必须确定令牌需要“多么独特”。 考虑熵的全部也是唯一的要点是确保系统可以抵抗暴力攻击:可能值的空间必须足够大,以至于任何攻击者在非荒谬的时间内1只能尝试忽略不计的值。 唯一标识符(例如随机UUID
具有122位的熵(即2 ^ 122 = 5.3x10 ^ 36)-碰撞的机率是“ *(...),因为十亿分之一的重复机率是103万亿版本4 UUID必须生成2 “。 我们将选择128位,因为它恰好适合16个字节,并且被视为足以在几乎所有情况下都具有唯一性,但是在最极端的用例中,您不必考虑重复项。 这是一个简单的熵比较表,其中包括对生日问题的简单分析。
对于简单的要求,8或12个字节的长度就足够了,但是对于16个字节,您就处于“安全方面”。
基本上就是这样。 最后一件事是考虑编码,以便可以将其表示为可打印的文本(读取为String
)。
典型的编码包括:
Base64
每个字符编码为6位,从而产生33%的开销。 幸运的是,在Java 8+和Android中有标准实现。 使用较旧的Java,您可以使用众多第三方库中的任何一个。 如果您希望令牌是网址安全的,请使用RFC4648的网址安全版本(大多数实现通常都支持该版本)。 使用填充编码16个字节的示例: XfJhfv3C0P6ag7y9VQxSbw==
Base32
每个字符都编码5位,从而产生40%的开销。 这将使用AZ
和2-7
从而使其在空间上有效,同时不区分大小写字母数字。 JDK中没有标准实现 。 编码不带填充的16个字节的示例: WUPIL5DQTZGMF4D3NX5L7LNFOY
Base16
(十六进制)每个字符编码4位,每个字节需要2个字符(即16个字节创建一个长度为32的字符串)。 因此,十六进制的空间效率不及Base32
但在大多数情况下(url)都可以安全使用,因为它仅使用0-9
和A
至F
编码16个字节的示例: 4fa3dd0f57cb3bf331441ed285b27735
。 在此处查看有关转换为十六进制的SO讨论。
存在诸如Base85和奇异的Base122之类的其他编码,其空间效率更高/更差。 您可以创建自己的编码(基本上该线程中的大多数答案都可以做到),但是如果您没有非常具体的要求,我建议您不要这样做。 请参阅Wikipedia文章中的更多编码方案。
SecureRandom
hex
或base32
) 别
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
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
如果您想使用现成的cli工具,可以使用骰子: https : //github.com/patrickfav/dice
如果您已经有一个ID可以使用(例如,实体中的合成long
),但是不想发布内部值 ,则可以使用此库对其进行加密和混淆: https : //github.com/帕特里克·法夫
IdMask idMask = IdMasks.forLongIds(Config.builder(key).build());
String maskedId = idMask.mask(id);
//example: NPSBolhMyabUBdTyanrbqT8
long originalId = idMask.unmask(maskedId);
真的不喜欢关于“简单”解决方案的任何答案: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());
}
@Test
public void buildFiveRandomStrings() {
for (int q = 0; q < 5; q++) {
System.out.println(randomString(10, "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"));//charachterSet can basically be anything
}
}
或(较易读的旧方法)
public String randomString(int length, String characterSet) {
StringBuilder sb = new StringBuilder(); //consider using StringBuffer if needed
for (int i = 0; i < length; i++) {
int randomInt = new SecureRandom().nextInt(characterSet.length());
sb.append(characterSet.substring(randomInt, randomInt + 1));
}
return sb.toString();
}
@Test
public void buildFiveRandomStrings() {
for (int q = 0; q < 5; q++) {
System.out.println(randomString(10, "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")); //charachterSet can basically be anything
}
}
但另一方面,您也可以使用具有相当好的熵的UUID( https://en.wikipedia.org/wiki/Universally_unique_identifier#Collisions ):
UUID.randomUUID().toString().replace("-", "")
希望能有所帮助。
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 SecureRandom random = new SecureRandom();
for (int i = 0; i < length; i++) {
randomString = randomString + chars[random.nextInt(chars.length)];
}
return randomString;
}
该代码工作正常。 如果使用这种方法,我建议您使用10个以上的字符。 碰撞发生在5个字符/ 30362次迭代中。 这花了9秒钟。
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
您提到“简单”,但是万一其他人在寻找满足更严格安全要求的东西,您可能想看看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