Guida all'UUID in Java

1. Panoramica

UUID (Universally Unique Identifier), noto anche come GUID (Globally Unique Identifier), rappresenta un valore lungo 128 bit che è univoco per tutti gli scopi pratici . La rappresentazione standard dell'UUID utilizza cifre esadecimali (ottetti):

123e4567-e89b-12d3-a456-556642440000

Un UUID è composto da cifre esadecimali (4 caratteri ciascuna) insieme a 4 simboli "-" che rendono la sua lunghezza pari a 36 caratteri .

Il Nil UUID è una forma speciale di UUID in cui tutti i bit sono impostati su zero.

In questo articolo, daremo uno sguardo alla classe UUID in Java. Per prima cosa, vedremo come utilizzare la classe stessa. Quindi, esamineremo i diversi tipi di UUID e come possiamo generarli in Java.

2. La classe UUID

La classe UUID ha un unico costruttore:

UUID uuid = new UUID(long mostSignificant64Bits, long leastSignificant64Bits);

Se vogliamo usare questo costruttore, dobbiamo fornire due valori lunghi. Tuttavia, ci richiede di costruire noi stessi il modello di bit per l'UUID.

Per comodità, ci sono tre metodi statici per creare un UUID. Questi sono:

UUID uuid = UUID.nameUUIDFromBytes(byte[] bytes); 

Questo metodo crea un UUID versione 3 dall'array di byte specificato.

UUID uuid = UUID.randomUUID(); 

Il metodo randomUUID () crea un UUID versione 4. Questo è il modo più conveniente per creare un UUID.

UUID uuid = UUID.fromString(String uuidHexDigitString); 

Il terzo metodo statico restituisce un oggetto UUID data la rappresentazione di stringa di un dato UUID.

Vediamo ora come è strutturato un UUID.

3. Struttura

Prendiamo l'esempio UUID:

123e4567-e89b-42d3-a456-556642440000 xxxxxxxx-xxxx-Bxxx-Axxx-xxxxxxxxxxxx

3.1. Variante UUID

A rappresenta la variante che determina il layout dell'UUID. Tutti gli altri bit nell'UUID dipendono dall'impostazione dei bit nel campo variante. La variante è determinata da 3 bit più significativi di A:

 MSB1 MSB2 MSB3 0 X X reserved (0) 1 0 X current variant (2) 1 1 0 reserved for Microsoft (6) 1 1 1 reserved for future (7)

Il valore di A nell'UUID menzionato è "a". L'equivalente binario di 'a' (= 10xx) mostra la variante come 2.

3.2. Versione UUID

B rappresenta la versione. La versione nell'UUID menzionato (valore di B ) è 4.

Java fornisce metodi per ottenere la variante e la versione dell'UUID:

UUID uuid = UUID.randomUUID(); int variant = uuid.variant(); int version = uuid.version();

Queste sono 5 diverse versioni per la variante 2 UUID: Time Based (UUIDv1), DCE Security (UUIDv2), Name Based (UUIDv3 e UUIDv5), Random (UUIDv4).

Java fornisce un'implementazione per v3 e v4, ma fornisce anche un costruttore per la generazione di qualsiasi tipo di UUID:

UUID uuid = new UUID(long mostSigBits, long leastSigBits);

4. Le versioni UUID

4.1. Versione 1

La versione 1 dell'UUID si basa sul timestamp corrente, misurato in unità di 100 nanosecondi dal 15 ottobre 1582, concatenato con l'indirizzo MAC del dispositivo in cui è stato creato l'UUID.

Se la privacy è un problema, la versione 1 dell'UUID può essere generata in alternativa con un numero casuale a 48 bit invece dell'indirizzo MAC.

In questo articolo, faremo questa alternativa. Innanzitutto, genereremo i 64 bit meno e più significativi come valori lunghi:

private static long get64LeastSignificantBitsForVersion1() { Random random = new Random(); long random63BitLong = random.nextLong() & 0x3FFFFFFFFFFFFFFFL; long variant3BitFlag = 0x8000000000000000L; return random63BitLong + variant3BitFlag; } private static long get64MostSignificantBitsForVersion1() { LocalDateTime start = LocalDateTime.of(1582, 10, 15, 0, 0, 0); Duration duration = Duration.between(start, LocalDateTime.now()); long seconds = duration.getSeconds(); long nanos = duration.getNano(); long timeForUuidIn100Nanos = seconds * 10000000 + nanos * 100; long least12SignificatBitOfTime = (timeForUuidIn100Nanos & 0x000000000000FFFFL) >> 4; long version = 1 << 12; return (timeForUuidIn100Nanos & 0xFFFFFFFFFFFF0000L) + version + least12SignificatBitOfTime; }

Possiamo quindi passare questi due valori al costruttore dell'UUID:

public static UUID generateType1UUID() { long most64SigBits = get64MostSignificantBitsForVersion1(); long least64SigBits = get64LeastSignificantBitsForVersion1(); return new UUID(most64SigBits, least64SigBits); }

4.2. Versione 2

La versione 2 si basa su un timestamp e anche sull'indirizzo MAC. Tuttavia, RFC 4122 non specifica i dettagli esatti della generazione, pertanto non esamineremo un'implementazione in questo articolo.

4.3. Versione 3 e 5

Gli UUID vengono generati utilizzando l'hash dello spazio dei nomi e del nome. Gli identificatori dello spazio dei nomi sono UUID come Domain Name System (DNS), Object Identifiers (OID), URL, ecc.

UUID = hash(NAMESPACE_IDENTIFIER + NAME)

L'unica differenza tra UUIDv3 e UUIDv5 è l'algoritmo di hashing: la v3 utilizza MD5 (128 bit) mentre la v5 utilizza SHA-1 (160 bit).

In poche parole, tronciamo l'hash risultante a 128 bit e quindi sostituiamo 4 bit per la versione e 2 bit per la variante.

Generiamo l'UUID di tipo 3:

byte[] nameSpaceBytes = bytesFromUUID(namespace); byte[] nameBytes = name.getBytes("UTF-8"); byte[] result = joinBytes(nameSpaceBytes, nameBytes); UUID uuid = UUID.nameUUIDFromBytes(result);

Qui, è importante notare che la stringa esadecimale per lo spazio dei nomi deve prima essere convertita in un array di byte.

Java non fornisce l'implementazione per il tipo 5. Controlla il nostro repository di codice sorgente per l'UUIDv5.

4.4. Versione 4

L'implementazione dell'UUID v4 utilizza numeri casuali come origine. L'implementazione Java è SecureRandom , che utilizza un valore imprevedibile come seme per generare numeri casuali per ridurre la possibilità di collisioni.

Generiamo l'UUID della versione 4:

UUID uuid = UUID.randomUUID();

Generiamo una chiave univoca utilizzando "SHA-256" e un UUID casuale:

MessageDigest salt = MessageDigest.getInstance("SHA-256"); salt.update(UUID.randomUUID().toString().getBytes("UTF-8")); String digest = bytesToHex(salt.digest());

5. conclusione

In questo articolo abbiamo visto come è strutturato un UUID, quali varianti e versioni ci sono. Abbiamo appreso per quali versioni Java fornisce un'implementazione pronta all'uso e abbiamo esaminato esempi di codice per generare le altre versioni.

E, come sempre, il codice sorgente dell'implementazione è disponibile su Github.