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


1741

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

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



58
即使考虑到生日悖论,如果您使用12个字母数字字符(共62个字符),则仍需要超过340亿个字符串才能达到这一悖论。生日悖论无论如何也不能保证发生碰撞,只是说发生碰撞的可能性超过50%。
NullUserException 2012年

4
@NullUserException 50%的成功机会(每次尝试)非常高:即使尝试10次,成功率为0.999。有了这个事实,并且您可以在24小时内尝试很多,因此您不需要340亿个字符串就可以肯定地猜出其中至少一个。这就是为什么某些会话令牌应该非常长的原因。
Pijusn 2015年

16
我猜这3个单行代码非常有用Long.toHexString(Double.doubleToLongBits(Math.random())); UUID.randomUUID().toString(); RandomStringUtils.randomAlphanumeric(12);
。– Manindar

18
@Pijusn我知道这很老了,但是...生日悖论中的“ 50%机会” 不是 “每次尝试”,而是“(在这种情况下)340亿个字符串中有50%的机会存在”至少一对重复”。你需要1.6 九月 100万年- 1.6e21 -输入你的数据库,以便在那里每试50%的机会。
锡精灵19年

Answers:


1541

算法

要生成随机字符串,请连接从一组可接受的符号中随机抽取的字符,直到字符串达到所需的长度为止。

实作

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

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并非不可预测,因此不应用作会话标识符。

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

UUID不灵活;它们具有标准化的结构和布局。这是他们的主要美德,也是他们的主要弱点。与外部方合作时,UUID提供的标准化可能会有所帮助。仅用于内部使用,它们可能效率很低。


6
如果您需要空格,则可以在行.replaceAll("\\d", " ");的末尾进行 return new BigInteger(130, random).toString(32);正则表达式交换。它用空格替换所有数字。非常适合我:我用它代替前端Lorem Ipsum
weisjohn 2011年

4
@weisjohn这是一个好主意。您可以使用第二种方法执行类似的操作,方法是删除数字symbols并使用空格代替;您可以通过更改符号中的空格数来控制“单词”的平均长度(出现次数越多表示越短)。对于真正的顶级假文本解决方案,您可以使用马尔可夫链!
erickson

4
这些标识符是从一定大小的空间中随机选择的。它们可以是1个字符长。如果需要固定长度,则可以使用第二种解决方案,将SecureRandom实例分配给random变量。
erickson

15
为什么使用.toString(32)而不是.toString(36)?
ejain 2012年

17
@ejain因为32 = 2 ^ 5; 每个字符将恰好代表5位,而130位可以平均分成几个字符。
erickson '02

817

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

33
请注意,此解决方案仅生成带有十六进制字符的随机字符串。在某些情况下这可能很好。
戴夫

5
UUID类很有用。但是,它们不像我的答案产生的标识符那么紧凑。例如,这可能是一个问题。取决于您的需求。
erickson

6
@Ruggs-目标是字母数字字符串。如何将输出扩展到任何可能的字节呢?
erickson

72
根据RFC4122,使用UUID作为令牌是一个坏主意:不要以为UUID很难猜测;例如,它们不应用作安全功能(仅拥有所有权即可授予访问权限的标识符)。可预测的随机数源将加剧这种情况。ietf.org/rfc/rfc4122.txt
Somatik

34
UUID.randomUUID().toString().replaceAll("-", "");根据要求使字符串为字母数字。
Numid 2014年

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

61
+1,这是生成指定长度的随机字符串的最简单解决方案(除了使用Commons Lang的RandomStringUtils)。
Jonik 2012年

12
考虑使用SecureRandom代替Random类。如果在服务器上生成密码,则可能容易受到定时攻击。
foens 2014年

8
我还要加上小写字母: AB = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";和其他一些允许的字符。
ACV 2015年

1
为什么不放入static Random rnd = new Random();方法内部?
微小的

4
@MicroR是否有充分的理由Random在每次方法调用中创建对象?我不这么认为。
cassiomolin '16

484

如果您愿意使用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不推荐使用。


22
刚刚浏览了提到Apache Commons Lang 3.3.1 -它仅java.util.Random用于提供随机序列,因此它会产生不安全的序列
Yuriy Nakonechnyy 2014年

16
确保在使用RandomStringUtils时使用SecureRandom:public static java.lang.String random(int count, int start, int end, boolean letters, boolean numbers, @Nullable char[] chars, java.util.Random random)
Ruslans Uralovs 2015年

不使用。这会产生不安全的序列
帕特里克·法夫尔

109

您可以为此使用Apache库:RandomStringUtils

RandomStringUtils.randomAlphanumeric(20).toUpperCase();

18
@kamil,我查看了RandomStringUtils的源代码,它使用实例化的java.util.Random实例,而没有参数。java.util.Random的文档说,如果没有提供种子,它将使用当前系统时间。这意味着它不能用于会话标识符/密钥,因为攻击者可以轻松预测在任何给定时间生成的会话标识符是什么。
Inshallah 2012年

36
@Inshallah:您(不必要地)过度设计了系统。虽然我同意它使用时间作为种子,但是攻击者必须有权访问以下数据才能真正获得他想要的东西1.时间到确切的毫秒数(代码被植入种子时)2.到目前为止发生的调用次数3.自己的通话具有原子性(因此,到目前为止的通话次数相同)如果攻击者同时拥有这三样东西,那么您手头的问题就大得多了
Ajeet Ganga 2013年

3
gradle依赖关系: compile 'commons-lang:commons-lang:2.6'
younes0

4
@Ajeet这不是真的。您可以从随机数生成器的输出中得出其状态。如果攻击者可以生成数千次调用以生成随机API令牌,则攻击者将能够预测所有将来的API令牌。
Thomas Grainger

3
@AjeetGanga与工程无关。如果要创建会话ID,则需要一个加密的伪随机生成器。每个使用时间作为种子的项目都是可预测的,并且对于应该不可预测的数据来说是非常不安全的。只需使用就可以了SecureRandom
Patrick Favre

105

一行:

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

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


9
但只有6个字母:(
Moshe Revah 2011年

2
它也对我有帮助,但只有十六进制数字:(
noquery 2011年

@Zippoxer,您可以多次
关注

7
OP的示例显示以下字符串作为示例AEYGF7K0DM1X,该字符串不是十六进制。令我担心的是,人们经常将字母数字错误与十六进制错误。它们不是同一件事。
hfontanez 2014年

6
这比给定字符串长度(应Math.random()产生double介于0和1之间)的随机性要少得多,因此指数部分大部分未被使用。使用random.nextLong一个随机long的,而不是这个丑陋的黑客。
maaartinus 2015年

80

无需任何外部库即可轻松实现。

1.加密伪随机数据生成

首先,您需要加密的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万亿版本必须生成2个 4 UUID 。我们将选择128位,因为它恰好适合16个字节,并且被视为足够因为对于几乎所有(但最极端的)用例而言,它都是唯一的,因此您不必考虑重复项。这是一个简单的熵比较表,其中包括对生日问题的简单分析。

代币大小的比较

对于简单的要求,8或12个字节的长度就足够了,但是对于16个字节,您就处于“安全方面”。

基本上就是这样。最后一件事是考虑编码,以便可以将其表示为可打印的文本(读取为a String)。

3.二进制到文本编码

典型的编码包括:

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

  • Base32每个字符编码5位,产生40%的开销。这将使用大小写不敏感的字母数字,A-Z2-7使其具有合理的空间效率。JDK中没有标准实现。编码不带填充的16个字节的示例:WUPIL5DQTZGMF4D3NX5L7LNFOY

  • Base16(十六进制)每个字符编码4位,每个字节需要2个字符(即16个字节创建一个长度为32的字符串)。因此,十六进制的空间效率不及十六进制,Base32但在大多数情况下(url)仅可使用0-9A,因此可以安全使用F。编码16个字节的示例:4fa3dd0f57cb3bf331441ed285b27735在此处查看有关转换为十六进制的SO讨论。

存在诸如Base85和奇异的Base122之类的其他编码,其空间效率更高/更差。您可以创建自己的编码(基本上该线程中的大多数答案都可以做到),但是如果您没有非常具体的要求,我建议您不要这样做。请参阅Wikipedia文章中的更多编码方案。

4.总结和例子

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

  • ...使用您的家用Brew编码:如果其他人看到您使用哪种标准编码,而不是一次一次创建字符的怪异循环,则可以更好地维护和阅读。
  • ...使用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); //hex encoding
}

//generateRandomHexToken(16) -> 2189df7475e96aa3982dbeab266497cd

示例:Base64令牌生成器(网址安全)

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工具,可以使用骰子:https : //github.com/patrickfav/dice

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

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

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

3
这个答案是完整的,并且无需添加任何依赖即可使用。如果要避免在输出中出现负号,可以BigInteger使用构造函数参数BigInteger(1, token)代替来防止使用s BigInteger(token)
francoisr

坦克@francoisr作为提示,我编辑了代码示例
Patrick Favre

import java.security.SecureRandom;并且import java.math.BigInteger;需要使示例工作,但效果很好!
anothermh

好的答案,但是/ dev / random是一种阻止方法,这就是为什么如果熵太低的话,它会慢到阻止的程度。更好的非阻塞方法是/ dev / urandom。可以通过<jre> /lib/security/java.security进行配置,并设置securerandom.source = file:/ dev /./ urandom
Muzammil,

@Muzammil参见tersesystems.com/blog/2015/12/17/…(也在答案中链接)- new SecureRandom()使用/dev/urandom
Patrick Favre

42

使用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

是否可以将SecureRandom与shuffle一起使用?
iwein

34

在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

4
这将产生不安全的序列,即容易猜到的序列。
Yuriy Nakonechnyy 2014年

8
所有这些双重感染的随机int生成都被设计破坏,缓慢且不可读。使用Random#nextIntnextLongSecureRandom如果需要,请切换到。
maaartinus

31

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

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条记录。


39
有人建议您,大约比您早一年。
erickson

31

一种简短的解决方案,但仅使用小写和数字:

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

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


11
请记住,结果前面有一个负号的可能性为50%!因此,如果您不希望减号,可以将r.nextLong()包装在Math.abs()中:Long.toString(Math.abs(r.nextLong()), 36);
Ray

5
@RayHulha:如果您不想减号,则应将其剪掉,因为令人惊讶的是,Math.abs为Long.MIN_VALUE返回了负值。
用户未知

有趣的是Math.abs返回负数。更多信息,请访问:bmaurer.blogspot.co.nz/2006/10/…–
Phil

1
abs通过使用按位运算符清除最高有效位可以解决此问题。这将适用于所有值。
Radiodef '18 -4-2

1
@Radiodef本质上就是@userunkown所说的。我想你也可以做<< 1 >>> 1
shmosel

15

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

3
这是伟大的-但是如果你想保持严格的字母数字(0-9,AZ,AZ)看到这里rationaljava.com/2015/06/...

12

使用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提供了一个生成随机数的构造函数,该随机数均匀分布在range上0 to (2^numBits - 1)。不幸的是35不是2 ^ numBits-1可以接收的数字。因此,我们有两个选择:与2^5-1=31或一起使用2^6-1=63。如果我们选择的话,2^6我们会得到很多“不必要” /“更长”的数字。因此2^5,即使我们丢失4个字符(wz),也是更好的选择。现在要生成一定长度的字符串,我们可以简单地使用2^(length*numBits)-1数。最后一个问题是,如果我们想要一个具有一定长度的字符串,则random可能会生成一个较小的数字,因此长度无法满足,因此我们必须将该字符串填充到所需的长度(以零开头)。


您能更好地解释5吗?
朱利安·苏亚雷斯

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

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

所以这只是将密码添加到字符串中,是的,可以很好地检查它,非常简单。我写的


我允许自己做一些小的修改。您为什么+ 0经常添加?为什么要拆分即席声明和初始声明?索引1、2、3、4而不是0、1、2、3的优点是什么?最重要的是:您获取了一个随机值,并与if-else新值进行了4倍比较,新值可能总是不匹配,而不会获得更多的随机性。但是请随时回滚。
用户未知

8

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

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

我不认为完全基于概率的重复令牌失败是不公平的。
Thom Wiggers 2012年

8
  1. 根据您的要求更改字符串字符。

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

3
这并没有增加以前未提供的数十个答案。Random在循环的每次迭代中创建新实例的效率都很低。
erickson 2014年

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

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

7

真的不喜欢关于“简单”解决方案的任何答案: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("-", "")

希望能有所帮助。


6

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


谢谢,修复它。因此,至少有源并且链接有效。不利的一面是,虽然我看到pwgen最近才更新过,但看起来好像并没有更新过。
michaelok

4

您可以将UUID类与其getLeastSignificantBits()消息一起使用,以获取64位的Random数据,然后将其转换为基数为36的数字(即,由0-9,AZ组成的字符串):

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

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


2
在世界上为什么要使用UUID来获取随机位?为什么不只是使用random.nextLong()?甚至Double.doubleToLongBits(Math.random())
erickson

4

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

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

}

4

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



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

2
该字符串连接不必要地效率低下。疯狂的缩进使您的代码几乎不可读。这与杰米的想法相同但执行不力。
erickson

3

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

 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秒钟。


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

1
非常好!但是它应该length代替chars.lengthfor循环: for (int i = 0; i < length; i++)
焚化炉

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.