如何在 Java 中创建用户友好的唯一 ID、UUID 或其他唯一标识符

发布于 2024-10-11 10:24:19 字数 465 浏览 6 评论 0原文

我通常使用 UUID 类来生成唯一 ID。如果这些 ID 仅由技术系统使用,则效果很好,它们不关心它们有多长:

System.out.println(UUID.randomUUID().toString());

> 67849f28-c0af-46c7-8421-94f0642e5d4d

是否有一种好方法来创建比 UUID 短一点的用户友好的唯一 ID(例如来自tinyurl 的 ID)?用例:您希望通过邮件将 ID 发送给客户,然后客户访问您的网站并将该号码输入到表单中,例如优惠券 ID。

我假设 UUID 在 UUID 的 128 位范围的整个范围内均匀生成。那么,仅使用较低的 64 位是否明智呢?

System.out.println(UUID.randomUUID().getLeastSignificantBits());

欢迎任何反馈。

I usually use the UUID class to generate unique IDs. This works fine if these IDs are used by technical systems only, they don't care how long they are:

System.out.println(UUID.randomUUID().toString());

> 67849f28-c0af-46c7-8421-94f0642e5d4d

Is there a nice way to create user friendly unique IDs (like those from tinyurl) which are a bit shorter than the UUIDs? Usecase: you want to send out IDs via Mail to your customers which in turn visit your site and enter that number into a form, like a voucher ID.

I assume that UUIDs get generated equally through the whole range of the 128 Bit range of the UUID. So would it be sage to use just the lower 64 Bits for instance?

System.out.println(UUID.randomUUID().getLeastSignificantBits());

Any feedback is welcome.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

空城仅有旧梦在 2024-10-18 10:24:19

我假设 UUID 已生成
在整个范围内均等地
UUID 的 128 位范围。

首先,您的假设可能不正确,具体取决于 UUID 类型(1、2、3 或 4)。来自Java UUID 文档

存在不同的变体
这些全局标识符。方法
这个类的用于操纵
Leach-Salz 变体,尽管
构造函数允许创建任何
UUID 的变体(如下所述)。

变体 2 (Leach-Salz) 的布局
UUID如下: 最
显着长由
以下无符号字段:

0xFFFFFFFF00000000 time_low 
0x00000000FFFF0000 time_mid 
0x000000000000F000 version 
0x0000000000000FFF time_hi  

最不重要的长字符包括
以下无符号字段:

0xC000000000000000 variant 
0x3FFF000000000000 clock_seq 
0x0000FFFFFFFFFFFF node  

变体字段包含一个值
它标识了布局
UUID。上面描述的位布局
仅对具有以下内容的 UUID 有效
变量值为 2,表示
Leach-Salz 变体。

版本字段保存的值是
描述此 UUID 的类型。那里
有四种不同的基本类型
UUID:基于时间的、DCE 安全性、
基于名称且随机生成
UUID。这些类型都有一个版本
值分别为 1、2、3 和 4。

做你正在做的事情的最好方法是生成一个随机字符串,其代码看起来像这样(source ):

public class RandomString {

          public static String randomstring(int lo, int hi){
                  int n = rand(lo, hi);
                  byte b[] = new byte[n];
                  for (int i = 0; i < n; i++)
                          b[i] = (byte)rand('a', 'z');
                  return new String(b, 0);
          }

          private static int rand(int lo, int hi){
                      java.util.Random rn = new java.util.Random();
                  int n = hi - lo + 1;
                  int i = rn.nextInt(n);
                  if (i < 0)
                          i = -i;
                  return lo + i;
          }

          public static String randomstring(){
                  return randomstring(5, 25);
          }

        /**
         * @param args
         */
        public static void main(String[] args) {
                System.out.println(randomstring());

        }

}

如果您非常担心冲突或其他问题,我建议您对 UUID 进行 base64 编码,这样可以减少其大小。

这个故事的寓意是:不要依赖 UUID 的各个部分,因为它们是整体设计的。如果您确实需要依赖 UUID 的各个部分,请确保您熟悉特定的 UUID 类型和实现。

I assume that UUIDs get generated
equally through the whole range of the
128 Bit range of the UUID.

First off, your assumption may be incorrect, depending on the UUID type (1, 2, 3, or 4). From the Java UUID docs:

There exist different variants of
these global identifiers. The methods
of this class are for manipulating the
Leach-Salz variant, although the
constructors allow the creation of any
variant of UUID (described below).

The layout of a variant 2 (Leach-Salz)
UUID is as follows: The most
significant long consists of the
following unsigned fields:

0xFFFFFFFF00000000 time_low 
0x00000000FFFF0000 time_mid 
0x000000000000F000 version 
0x0000000000000FFF time_hi  

The least significant long consists of
the following unsigned fields:

0xC000000000000000 variant 
0x3FFF000000000000 clock_seq 
0x0000FFFFFFFFFFFF node  

The variant field contains a value
which identifies the layout of the
UUID. The bit layout described above
is valid only for a UUID with a
variant value of 2, which indicates
the Leach-Salz variant.

The version field holds a value that
describes the type of this UUID. There
are four different basic types of
UUIDs: time-based, DCE security,
name-based, and randomly generated
UUIDs. These types have a version
value of 1, 2, 3 and 4, respectively.

The best way to do what you're doing is to generate a random string with code that looks something like this (source):

public class RandomString {

          public static String randomstring(int lo, int hi){
                  int n = rand(lo, hi);
                  byte b[] = new byte[n];
                  for (int i = 0; i < n; i++)
                          b[i] = (byte)rand('a', 'z');
                  return new String(b, 0);
          }

          private static int rand(int lo, int hi){
                      java.util.Random rn = new java.util.Random();
                  int n = hi - lo + 1;
                  int i = rn.nextInt(n);
                  if (i < 0)
                          i = -i;
                  return lo + i;
          }

          public static String randomstring(){
                  return randomstring(5, 25);
          }

        /**
         * @param args
         */
        public static void main(String[] args) {
                System.out.println(randomstring());

        }

}

If you're incredibly worried about collisions or something, I suggest you base64 encode your UUID which should cut down on its size.

Moral of the story: don't rely on individual parts of UUIDs as they are holistically designed. If you do need to rely on individual parts of a UUID, make sure you familiarize yourself with the particular UUID type and implementation.

南城追梦 2024-10-18 10:24:19

这是生成用户友好 ID 的另一种方法:
http://thedailywtf.com/Articles/The-Automated-Curse-Generator.aspx

(但是你应该使用坏词过滤器)

Here is another approach for generating user friendly IDs:
http://thedailywtf.com/Articles/The-Automated-Curse-Generator.aspx

(But you should go for the bad-word-filter)

眼波传意 2024-10-18 10:24:19

任何 UUID/Guid 只是 16 字节的数据。这 16 个字节可以使用 BASE64(或 BASE64url)轻松编码,然后去掉字符串末尾的所有“=”字符。

这给出了一个漂亮的短字符串,它仍然保存与 UUID/Guid 相同的数据。换句话说,如果有必要,可以从该数据重新创建 UUID/Guid。

Any UUID/Guid is just 16 Bytes of data. These 16 bytes can be easily encoded using BASE64 (or BASE64url), then stripped off all of the "=" characters at the end of the string.

This gives a nice, short string which still holds the same data as the UUID/Guid. In other words, it is possible to recreate the UUID/Guid from that data if such becomes necessary.

塔塔猫 2024-10-18 10:24:19

以下是生成 URL 友好的 22 字符 UUID 的方法

public static String generateShortUuid() {
        UUID uuid = UUID.randomUUID();

        long lsb = uuid.getLeastSignificantBits();
        long msb = uuid.getMostSignificantBits();

        byte[] uuidBytes = ByteBuffer.allocate(16).putLong(msb).putLong(lsb).array();

        // Strip down the '==' at the end and make it url friendly   
        return Base64.encode(uuidBytes)
                    .substring(0, 22)
                    .replace("/", "_")
                    .replace("+", "-");
    }

对于您的用例,最好跟踪注册用户的运行计数,并为每个值生成如下字符串令牌:

public static String longToReverseBase62(long value /* must be positive! */) {

        final char[] LETTERS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".toCharArray();

        StringBuilder result = new StringBuilder(9);
        do {
            result.append(LETTERS[(int)(value % 62)]);
            value /= 62l;
        }
        while (value != 0);

        return result.toString();
    }

出于安全原因,它如果您将这些值设置为非顺序的,效果会更好,因此每次用户注册时,您都可以将该值增加 1024(这可以很好地为 2^64 / 2^10 = 2^54 个用户生成 uuid,其中肯定比您需要的更多:)

Here's a way to generate a URL-friendly 22-character UUID

public static String generateShortUuid() {
        UUID uuid = UUID.randomUUID();

        long lsb = uuid.getLeastSignificantBits();
        long msb = uuid.getMostSignificantBits();

        byte[] uuidBytes = ByteBuffer.allocate(16).putLong(msb).putLong(lsb).array();

        // Strip down the '==' at the end and make it url friendly   
        return Base64.encode(uuidBytes)
                    .substring(0, 22)
                    .replace("/", "_")
                    .replace("+", "-");
    }

For your use-case, it would be better to track a running count of registered user, and for each value, generate a string-token like this:

public static String longToReverseBase62(long value /* must be positive! */) {

        final char[] LETTERS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".toCharArray();

        StringBuilder result = new StringBuilder(9);
        do {
            result.append(LETTERS[(int)(value % 62)]);
            value /= 62l;
        }
        while (value != 0);

        return result.toString();
    }

For security reasons, it would be better if you make the values non-sequential, so each time a user registers, you can increment the value let's say by 1024 (This would be good to generate uuids for 2^64 / 2^10 = 2^54 users which is quite certainly more than you'd ever need :)

找回味觉 2024-10-18 10:24:19

在撰写本文时,该问题的标题是:

如何在 Java 中创建用户友好的唯一 ID、UUID 或其他唯一标识符

生成用户友好的 ID 的问题是一个主观问题。如果您有一个唯一值,则有多种方法可以将其格式化为“用户友好”的值,并且它们都归结为将唯一值与“用户友好”ID 一对一映射 - 如果输入值是唯一的,“用户友好的”ID同样是唯一的。

此外,通常不可能创建一个唯一的随机值,至少每个随机值都是独立生成的。此外,如果您想生成唯一标识符,您应该问自己很多事情(来自我的 关于唯一随机标识符的部分):

  1. 应用程序能否轻松检查标识符在所需范围和范围内的唯一性(例如,检查具有该标识符的文件或数据库记录是否已存在)?
  2. 应用程序能否容忍为不同资源生成相同标识符的风险?
  3. 标识符是否必须难以猜测、只是“看起来随机”,还是两者都不是?
  4. 最终用户是否必须输入标识符或以其他方式转发标识符?
  5. 标识符标识的资源是否可供任何知道该标识符的人使用(即使没有登录或以某种方式授权)?
  6. 标识符必须容易记住吗?

就您而言,您有几个相互冲突的目标:您希望标识符是唯一的、随机的并且易于最终用户输入。但您应该考虑的其他事情是:

  • 只要其他用户知道 ID,是否就允许他们访问由 ID 标识的资源?如果不是,则需要额外的访问控制或更长的密钥长度。
  • 您的应用程序可以承受重复密钥的风险吗?如果是这样,那么密钥可以完全随机生成(例如通过加密 RNG,例如 Java 中的 java.security.SecureRandom)。如果没有,那么您的目标将更难实现,特别是对于用于安全目的的密钥。

此外,如果您希望最终用户必须输入 ID,则应考虑仔细选择字符集或 允许检测输入错误

At the time of this writing, this question's title is:

How to create user friendly unique IDs, UUIDs or other unique identifiers in Java

The question of generating a user-friendly ID is a subjective one. If you have a unique value, there are many ways to format it into a "user-friendly" one, and they all come down to mapping unique values one-to-one with "user-friendly" IDs — if the input value was unique, the "user-friendly" ID will likewise be unique.

In addition, it's not possible in general to create a random value that's also unique, at least if each random value is generated independently of any other. In addition, there are many things you should ask yourself if you want to generate unique identifiers (which come from my section on unique random identifiers):

  1. Can the application easily check identifiers for uniqueness within the desired scope and range (e.g., check whether a file or database record with that identifier already exists)?
  2. Can the application tolerate the risk of generating the same identifier for different resources?
  3. Do identifiers have to be hard to guess, be simply "random-looking", or be neither?
  4. Do identifiers have to be typed in or otherwise relayed by end users?
  5. Is the resource an identifier identifies available to anyone who knows that identifier (even without being logged in or authorized in some way)?
  6. Do identifiers have to be memorable?

In your case, you have several conflicting goals: You want identifiers that are unique, random, and easy to type by end users. But other things you should think about are:

  • Are other users allowed to access the resource identified by the ID, whenever they know the ID? If not, then additional access control or a longer key length will be necessary.
  • Can your application tolerate the risk of duplicate keys? If so, then the keys can be completely randomly generated (such as by a cryptographic RNG such as java.security.SecureRandom in Java). If not, then your goal will be harder to achieve, especially for keys intended for security purposes.

Also, if you want IDs that have to be typed in by end users, you should consider choosing a character set carefully or allowing typing mistakes to be detected.

江湖彼岸 2024-10-18 10:24:19

只为你:):

private final static char[] idchars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".toCharArray();
private static String createId(int len) {
    char[] id = new char[len];
    Random r = new Random(System.currentTimeMillis());
    for (int i = 0;  i < len;  i++) {
        id[i] = idchars[r.nextInt(idchars.length)];

    }
    return new String(id);
}

Only for you :) :

private final static char[] idchars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".toCharArray();
private static String createId(int len) {
    char[] id = new char[len];
    Random r = new Random(System.currentTimeMillis());
    for (int i = 0;  i < len;  i++) {
        id[i] = idchars[r.nextInt(idchars.length)];

    }
    return new String(id);
}
攒一口袋星星 2024-10-18 10:24:19

这个怎么样?实际上,此代码最多返回 13 个字符(数字和小写字母)。

import java.nio.ByteBuffer;
import java.util.UUID;

/**
 * Generate short UUID (13 characters)
 * 
 * @return short UUID
 */
public static String shortUUID() {
  UUID uuid = UUID.randomUUID();
  long l = ByteBuffer.wrap(uuid.toString().getBytes()).getLong();
  return Long.toString(l, Character.MAX_RADIX);
}

How about this one? Actually, this code returns 13 characters(numbers and lowercase alphabets) max.

import java.nio.ByteBuffer;
import java.util.UUID;

/**
 * Generate short UUID (13 characters)
 * 
 * @return short UUID
 */
public static String shortUUID() {
  UUID uuid = UUID.randomUUID();
  long l = ByteBuffer.wrap(uuid.toString().getBytes()).getLong();
  return Long.toString(l, Character.MAX_RADIX);
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文