我一直在寻找一种简单的Java 算法来生成伪随机字母数字字符串。在我的情况下,它将被用作唯一的会话/密钥标识符,“可能”在500K+一代人中是唯一的(我的需求实际上并不需要任何更复杂的东西)。
500K+
理想情况下,我将能够根据我的独特性需求指定长度。例如,生成的长度为 12 的字符串可能看起来像"AEYGF7K0DM1X".
"AEYGF7K0DM1X"
要生成随机字符串,请连接从一组可接受的符号中随机抽取的字符,直到字符串达到所需长度。
这是一些用于生成随机标识符的相当简单且非常灵活的代码。阅读以下信息以获取重要的应用说明。
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 提供的标准化可能会有所帮助。对于纯粹的内部使用,它们可能效率低下。