45 题: 如何生成随机字母数字字符串?

在...创建的问题 Fri, Feb 1, 2019 12:00 AM

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

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

    
1617
  1. 注意生日悖论
    2010-10-25 15:07:51Z
  2. 即使考虑到生日悖论,如果你使用12个字母数字字符(总共62个),你仍然需要超过340亿个字符串才能达到悖论。生日悖论无论如何都不能保证碰撞,只是说它有超过50%的机会。
    2012-10-29 04:13:10Z
  3. @ NullUserException 50%的成功几率(每次尝试)非常高:即使有10次尝试,成功率也是0.999。有了这个以及你可以在24小时内尝试A LOT的事实,你不需要340亿字符串来确定至少猜测其中一个。这就是为什么一些会话令牌应该真的很长。
    2015-01-31 10:28:30Z
  4. 这3个单行代码非常有用我想.. Long.toHexString(Double.doubleToLongBits(Math.random())); UUID.randomUUID().toString(); RandomStringUtils.randomAlphanumeric(12);
    2016-06-08 07:31:33Z
  5. @ Pijusn我知道这已经过时了,但......生日悖论中的“50%机会”是“每次尝试” ,“在(在这种情况下)(在这种情况下)有340亿个字符串,存在至少一对重复的”50%的可能性“。您需要1.6 sept illion - 1.6e21 - 数据库中的条目,以便每次尝试有50%的机会。
    2017-10-11 19:21:33Z
  6. 醇>
    30答案                              30 跨度>                         

    算法

    要生成随机字符串,请连接从可接受符号集中随机绘制的字符,直到字符串达到所需长度。

    实施

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

     
    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中或手动重新输入,则可能会导致编码问题。

    会话标识符的随机性或熵的基础来源应来自为加密而设计的随机数生成器。但是,初始化这些生成器有时可能计算成本高或速度慢,因此应尽可能重新使用它们。

    用作对象标识符

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

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

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

    解决这个问题表明,500k 15个字符标识符之间碰撞的可能性大约为2 -52 ,这可能不如宇宙射线未检测到的错误等。

    与UUID的比较

    根据他们的规范,UUID并非设计为不可预测,不应用作会话标识符。

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

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

        
    1488
    2019-02-11 01:18:21Z
    1. 如果您需要空格,可以将.replaceAll("\\d", " ");添加到return new BigInteger(130, random).toString(32);行的末尾以进行正则表达式交换。它用空格替换所有数字。对我来说很有用:我用这个作为前端Lorem Ipsum
      的替代品
      2011-10-07 15:00:05Z
    2. @ weisjohn这是一个好主意。您可以使用第二种方法执行类似操作,方法是从symbols中删除数字并使用空格代替;您可以通过更改符号中的空格数来控制平均“单词”长度(更短的单词出现次数)。对于一个非常过分的假文本解决方案,您可以使用马尔可夫链!
      2011-10-07 16:02:33Z
    3. 我试过了,但有时它只有31长。
      2011-12-19 23:46:09Z
    4. 为什么.toString(32)而不是.toString(36)?
      2012-02-21 19:13:40Z
    5. @ ejain因为32 = 2 ^ 5;每个字符恰好代表5位,130位可以均匀分为字符。
      2012-02-21 21:38:01Z
    6. 醇>

    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
    
        
    782
    2017-08-30 06:09:52Z
    1. 请注意,此解决方案仅生成带有十六进制字符的随机字符串。在某些情况下可以没问题。
      2011-05-05 09:28:17Z
    2. UUID类很有用。但是,它们不像我的答案所产生的标识符那么紧凑。这可能是一个问题例如,在URL中。取决于您的需求。
      2011-08-24 16:37:45Z
    3. @ Ruggs - 目标是字母数字字符串。如何将输出扩展到任何可能符合的字节?
      2011-10-07 16:18:53Z
    4. 根据RFC4122使用UUID作为标记是一个坏主意:不要认为UUID难以猜测;例如,它们不应被用作安全功能(仅仅拥有访问权限的标识符)。可预测的随机数源将加剧这种情况。 ietf.org/rfc/rfc4122.txt
      2012-12-31 11:31:04Z
    5. UUID.randomUUID().toString().replaceAll("-", "");根据要求生成字符串字母数字。
      2014-01-22 09:58:08Z
    6. 醇>
     
    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();
    }
    
        
    519
    2016-02-25 10:41:59Z
    1. + 1,这里最简单的解决方案是生成指定长度的随机字符串 (除了使用Commons Lang的RandomStringUtils)。
      2012-04-20 15:49:23Z
    2. 考虑使用SecureRandom而不是Random类。如果在服务器上生成密码,则可能容易受到计时攻击。
      2014-06-25 13:34:44Z
    3. 我还要添加小写:AB = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";和其他一些允许的字符。
      2015-09-07 20:56:11Z
    4. 为什么不把static Random rnd = new Random();放在方法里?
      2016-02-08 01:25:50Z
    5. @ MicroR是否有充分的理由在每个方法调用中创建Random对象?我不这么认为。
      2016-02-15 10:49:55Z
    6. 醇>

    如果您乐意使用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已被弃用。

        
    474
    2017-10-18 07:58:04Z
    1. 刚刚查看了Apache Commons Lang 3.3.1库的提到的类 - 它只使用java.util.Random来提供随机序列,因此它正在生成不安全的序列
      2014-04-03 14:51:29Z
    2. 确保在使用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)
      2015-03-03 13:28:02Z
    3. 请勿使用。这会创建不安全序列
      2019-04-04 13:03:27Z
    4. 醇>

    在一行中:

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

    http://mynotes.wordpress.com/2009 /07/23 /java的生成随机串/

        
    104
    2015-10-29 02:29:50Z
    1. 但只有6个字母:(
      2011-01-11 09:45:12Z
    2. 它也帮助了我,但只有十六进制数字:(
      2011-09-05 05:31:46Z
    3. @ Zippoxer,你可以多次连续=)
      2014-05-17 15:10:10Z
    4. OP的示例显示以下字符串作为示例AEYGF7K0DM1X,它不是十六进制的。它让我担心人们常常误用十六进制的字母数字。它们不是一回事。
      2014-11-20 02:31:59Z
    5. 这比给定字符串长度的随机性要小得多,因为Math.random()在0和1之间产生double,所以指数部分大多未使用。使用random.nextLong随机long而不是这个丑陋的黑客。
      2015-07-22 01:13:22Z
    6. 醇>

    您可以使用Apache库: RandomStringUtils

     
    RandomStringUtils.randomAlphanumeric(20).toUpperCase();
    
        
    100
    2014-12-09 14:15:43Z
    1. @ kamil,我查看了RandomStringUtils的源代码,它使用了一个没有参数实例化的java.util.Random实例。 java.util.Random的文档说如果没有提供种子,它会使用当前系统时间。这意味着它不能用于会话标识符/密钥,因为攻击者可以轻松预测在任何给定时间生成的会话标识符是什么。
      2012-09-26 10:14:26Z
    2. @ Inshallah:你(不必要地)过度使用系统。虽然我同意它使用时间作为种子,但是攻击者必须能够访问以下数据才能真正得到他想要的内容1.时间到精确的毫秒,代码被播种时2.到目前为止发生的调用次数3.他自己的电话的原子性(所以电话的数量 - 到目前为止仍然相同)如果你的攻击者拥有所有这三件事,那么你手头有更大的问题......
      2013-10-13 23:36:41Z
    3. gradle依赖:compile 'commons-lang:commons-lang:2.6'
      2015-01-19 14:35:30Z
    4. @ Ajeet这不是真的。您可以从其输出中导出随机数生成器的状态。如果攻击者可以生成几千个调用来生成随机API令牌,则攻击者将能够预测所有未来的API令牌。
      2016-12-20 13:52:09Z
    5. @ AjeetGanga与过度工程无关。如果要创建会话ID,则需要加密伪随机生成器。使用时间作为种子的每个prng都是可预测的,并且对于应该是不可预测的数据非常不安全。只需使用SecureRandom就可以了。
      2017-09-19 10:37:55Z
    6. 醇>

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

    1。加密伪随机数据生成

    首先,您需要加密PRNG。 Java有 SecureRandom ,因为它通常使用最好的机械上的熵源e(例如/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个字节,你就处于“安全的一面”。

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

    3。二进制到文本编码

    典型编码包括:

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

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

    • Base16 (十六进制)每个字符编码4bit,每字节需要2个字符(即16字节创建一个长度为32的字符串。因此,十六进制的空间效率低于Base32,但在大多数情况下(url)使用是安全的,因为它仅使用0-9AF。编码16字节的示例:4fa3dd0f57cb3bf331441ed285b27735请参阅此处有关转换为十六进制的SO讨论。

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

    4。摘要和示例

    • 使用 SecureRandom
    • 使用至少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); //hex 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工具,你可以使用骰子: https://github.com/patrickfav /骰子

        
    61
    2019-04-04 13:17:52Z
    1. 此答案已完成,无需添加任何依赖项即可运行。如果要避免输出中可能的减号,可以使用构造函数参数防止负BigIntegerBigInteger(1, token)而不是BigInteger(token)
      2017-07-11 07:50:45Z
    2. 坦克@francoisr的提示,我编辑了代码示例
      2017-07-11 08:38:39Z
    3. 需要import java.security.SecureRandom;import java.math.BigInteger;来使示例正常工作,但效果很好!
      2018-10-04 01:45:45Z
    4. 醇>

    使用美元应该很简单:

     
    // "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
    
        
    42
    2015-10-29 02:30:13Z
    1. 是否有可能将SecureRandom与shuffle一起使用?
      2016-11-16 10:58:23Z
    2. 醇>

    这是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
    
        
    32
    2008-09-03 17:16:38Z
    1. 这将产生不安全序列,即可以轻易猜到的序列。
      2014-04-03 14:53:58Z
    2. 和leftPad ????
      2015-03-19 17:53:19Z
    3. 所有这些双重感染的随机int生成都被设计破坏,缓慢且不可读。使用Random#nextIntnextLong。如果需要,请切换到SecureRandom
      2015-07-22 01:17:11Z
    4. 醇>

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

     
    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位是版本类型,2是变体,所以你得到122位随机。所以,如果你想要,你可以truncate从最后减小UUID的大小。这不是推荐的,但你仍然有很多随机性,足以让你的500k记录容易。

        
    32
    2014-04-16 11:49:00Z
    1. 有人提出建议,大约一年前。
      2013-09-10 04:49:58Z
    2. 醇>

    简单易用的解决方案,但只使用小写和数字:

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

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

        
    27
    2018-04-02 23:29:20Z
    1. 请记住,结果前面有一个减号的可能性为50%!因此,如果你不想要减号,可以使用在Math.abs()中包装r.nextLong():Long.toString(Math.abs(r.nextLong()), 36);
      2013-01-27 02:12:03Z
    2. @ RayHulha:如果你不想要减号,你应该将其剪掉,因为令人惊讶的是,Math.abs为Long.MIN_VALUE返回一个负值。
      2013-01-27 13:28:49Z
    3. 有趣的是Math.abs返回负数。更多信息: bmaurer.blogspot.co.nz/2006 /10 /...
      2013-11-10 20:34:20Z
    4. abs的问题通过使用按位运算符来清除最高有效位来解决。这适用于所有值。
      2018-04-02 23:27:35Z
    5. @ Radiodef这实际上是@userunkown所说的。我想你也可以做<< 1 >>> 1
      2018-04-02 23:35:00Z
    6. 醇>

    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();
    }
    
        
    15
    2018-10-03 19:09:43Z
    1. 这很棒 - 但是如果你想把它保持严格的字母数字(0-9,az,AZ),请看这里 rationaljava.com/2015/06 /...
      2015-06-23 14:08:13Z
    2. 醇>
     
    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;
    }
    
        
    11
    2012-10-09 04:47:13Z
    1. 它真的很健康。
      2016-09-02 08:41:31Z
    2. 醇>

    使用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和字符a-z的随机字符,我们需要一个random在0到35之间的数字,以获得每个字符之一。 BigInteger提供了一个构造函数,用于生成随机数,均匀分布在0 to (2^numBits - 1)范围内。不幸的是35不是2 ^ numBits可以接收的数字 - 1.所以我们有两个选择:要么选择2^5-1=31还是2^6-1=63。如果我们选择2^6,我们会得到很多“不必要的”/“更长”的数字。因此2^5是更好的选择,即使我们松散4个字符(w-z)。要生成一定长度的字符串,我们可以简单地使用2^(length*numBits)-1数字。最后一个问题,如果我们想要一个具有一定长度的字符串,随机可以生成一个小数字,所以不满足长度,所以我们必须将字符串填充到它所需的长度前置零。

        
    11
    2016-03-11 10:56:42Z
    1. 你能更好地解释5吗?
      2016-03-09 16:56:27Z
    2. 谢谢!那好多了!
      2016-03-11 11:50:27Z
    3. 醇>
     
    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);
        }
    }
    

    所以这样做只是将密码添加到字符串中......并且工作得很好看看...非常简单。我写了

        
    10
    2012-04-17 09:42:20Z
    1. 我允许自己做一些小修改。为什么经常添加+ 0?为什么要拆分现场和初始化的声明?索引1,2,3,4代替0,1,2,3的优点是什么?最重要的是:你取了一个随机值,并与if-else相比,新值的4倍,这可能总是不匹配,而不会获得更多的随机性。但随意回滚。
      2012-04-17 09:50:59Z
    2. 醇>

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

     
    /**
     * 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);
            }
        }
    }
    
        
    8
    2015-08-20 06:49:20Z
    1. 我认为单纯基于概率的重复令牌失败是不公平的。
      2012-06-02 15:22:33Z
    2. 醇>
     
    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;
        }
    }
    
        
    8
    2015-08-20 06:50:34Z
     
    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
    2015-08-20 06:51:18Z
    1. 根据您的要求更改字符串字符。

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

    3. 醇>

      结果

       
      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();
          }
      
          
    7
    2017-07-31 17:14:09Z
    1. 这没有增加先前未提及的数十个答案。在循环的每次迭代中创建一个新的Random实例是低效的。
      2014-02-10 05:17:52Z
    2. 醇>

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

    希望有所帮助。

        
    7
    2018-02-21 15:55:12Z

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

        
    6
    2017-06-26 22:47:13Z
    1. 2017-06-25 01:27:57Z
    2. 谢谢,修复它。所以它至少有源,链接是有效的。在缺点方面,虽然我看到pwgen最近已经更新,但它看起来似乎并没有在一段时间内更新。
      2017-06-26 22:50:50Z
    3. 醇>

    这是一个Scala解决方案:

     
    (for (i <- 0 until rnd.nextInt(64)) yield { 
      ('0' + rnd.nextInt(64)).asInstanceOf[Char] 
    }) mkString("")
    
        
    4
    2012-07-24 11:11:01Z

    您可以将UUID类及其getLeastSignificantBits()消息用于获取64位随机数据​​,然后将其转换为基数为36的数字(即包含0-9,A-Z的字符串):

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

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

        
    4
    2013-07-29 14:07:23Z
    1. 为什么世界上你会使用UUID获取随机位?为什么不使用random.nextLong()?甚至Double.doubleToLongBits(Math.random())
      2013-10-04 05:31:50Z
    2. 醇>

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

     
    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
    2015-08-20 06:52:26Z

    以下是 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 '-'
    
        
    4
    2019-01-29 06:07:59Z

    使用apache库它可以在一行中完成

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

    这里是doc http://commons .apache.org /郎/API-2.3 /组织/阿帕奇/公地/郎/RandomStringUtils.html

        
    3
    2012-10-15 07:52:12Z
    1. 已建议几个月前。
      2013-10-04 05:43:19Z
    2. 醇>
     
    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;
    }
    
        
    3
    2018-04-02 23:30:48Z
    1. 字符串连接不必要地低效。而疯狂的缩进使您的代码几乎无法读取。这与 Jamie的想法相同,但执行不力。
      2013-10-04 05:36:49Z
    2. 醇>

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

     
     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
    2018-11-26 18:31:13Z
     
    public static String getRandomString(int length) 
    {
       String randomStr = UUID.randomUUID().toString();
       while(randomStr.length() < length) {
           randomStr += UUID.randomUUID().toString();
       }
       return randomStr.substring(0, length);
    }
    
        
    2
    2012-12-03 06:59:05Z
    1. 这与给出的 Steve McLeod的回答几乎相同两年前。
      2013-10-04 05:40:15Z
    2. 醇>

    也许这很有用

     
    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;
    
        }
    }
    
        
    2
    2017-06-28 09:49:48Z
来源放置 这里