Come generare una stringa alfanumerica casuale?


1742

Ho cercato un semplice algoritmo Java per generare una stringa alfanumerica pseudo-casuale. Nella mia situazione sarebbe usato come un identificatore di sessione / chiave univoco che "probabilmente" sarebbe unico rispetto alla 500K+generazione (le mie esigenze non richiedono davvero nulla di molto più sofisticato).

Idealmente, sarei in grado di specificare una lunghezza in base alle mie esigenze di unicità. Ad esempio, una stringa generata di lunghezza 12 potrebbe assomigliare a qualcosa "AEYGF7K0DM1X".



58
Anche prendendo in considerazione il paradosso del compleanno, se usi 12 caratteri alfanumerici (62 in totale), avresti comunque bisogno di oltre 34 miliardi di stringhe per raggiungere il paradosso. E il paradosso del compleanno non garantisce comunque una collisione, dice solo che ha una probabilità superiore al 50%.
NullUserException,

4
@NullUserException 50% di probabilità di successo (per tentativo) è dannatamente alta: anche con 10 tentativi, la percentuale di successo è 0,999. Con questo e il fatto che puoi provare MOLTO in un periodo di 24 ore in mente, non hai bisogno di 34 miliardi di stringhe per essere abbastanza sicuro di indovinarne almeno una. Questo è il motivo per cui alcuni token di sessione dovrebbero essere molto, molto lunghi.
Pijusn,

16
Questi 3 codici a linea singola sono molto utili immagino ..Long.toHexString(Double.doubleToLongBits(Math.random())); UUID.randomUUID().toString(); RandomStringUtils.randomAlphanumeric(12);
Manindar,

18
@Pijusn So che questo è vecchio, ma ... la "probabilità del 50%" nel paradosso del compleanno NON è "a prova", è "50% di probabilità che, in (in questo caso) 34 miliardi di stringhe, esista a almeno una coppia di duplicati ". Avreste bisogno di 1.6 settembre ilioni - 1.6e21 - voci nel database in modo che ci sia una probabilità del 50% per ogni tentativo.
Stregone di stagno

Risposte:


1541

Algoritmo

Per generare una stringa casuale, concatena i caratteri disegnati in modo casuale dall'insieme di simboli accettabili fino a quando la stringa raggiunge la lunghezza desiderata.

Implementazione

Ecco un codice abbastanza semplice e molto flessibile per generare identificatori casuali. Leggi le informazioni che seguono per importanti note sull'applicazione.

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

}

Esempi di utilizzo

Crea un generatore non sicuro per identificatori di 8 caratteri:

RandomString gen = new RandomString(8, ThreadLocalRandom.current());

Crea un generatore sicuro per identificatori di sessione:

RandomString session = new RandomString();

Crea un generatore con codici di facile lettura per la stampa. Le stringhe sono più lunghe delle stringhe alfanumeriche complete per compensare l'utilizzo di un minor numero di simboli:

String easy = RandomString.digits + "ACEFGHJKLMNPQRUVWXYabcdefhijkprstuvwx";
RandomString tickets = new RandomString(23, new SecureRandom(), easy);

Utilizzare come identificatori di sessione

La generazione di identificativi di sessione che sono probabilmente univoci non è abbastanza buona, oppure potresti semplicemente usare un semplice contatore. Gli attaccanti dirottano le sessioni quando vengono utilizzati identificatori prevedibili.

C'è tensione tra lunghezza e sicurezza. Gli identificatori più brevi sono più facili da indovinare, perché ci sono meno possibilità. Ma gli identificatori più lunghi consumano più memoria e larghezza di banda. Un set più grande di simboli aiuta, ma potrebbe causare problemi di codifica se gli identificatori sono inclusi negli URL o reinseriti a mano.

La fonte sottostante di casualità, o entropia, per identificatori di sessione dovrebbe provenire da un generatore di numeri casuali progettato per la crittografia. Tuttavia, l'inizializzazione di questi generatori a volte può essere computazionalmente costosa o lenta, quindi è necessario fare uno sforzo per riutilizzarli quando possibile.

Utilizzare come identificatori di oggetti

Non tutte le applicazioni richiedono sicurezza. L'assegnazione casuale può essere un modo efficiente per più entità di generare identificatori in uno spazio condiviso senza alcun coordinamento o partizionamento. Il coordinamento può essere lento, specialmente in un ambiente cluster o distribuito, e la divisione di uno spazio causa problemi quando le entità finiscono con condivisioni troppo piccole o troppo grandi.

Gli identificatori generati senza prendere misure per renderli imprevedibili dovrebbero essere protetti con altri mezzi se un attaccante potrebbe essere in grado di visualizzarli e manipolarli, come accade nella maggior parte delle applicazioni web. Dovrebbe esistere un sistema di autorizzazione separato che protegga gli oggetti il ​​cui identificatore può essere indovinato da un utente malintenzionato senza autorizzazione di accesso.

Occorre inoltre prestare attenzione all'utilizzo di identificatori sufficientemente lunghi da rendere improbabili le collisioni, dato il numero totale previsto di identificatori. Questo è indicato come "il paradosso del compleanno". La probabilità di una collisione, p , è approssimativamente n 2 / (2q x ), dove n è il numero di identificatori effettivamente generati, q è il numero di simboli distinti nell'alfabeto e x è la lunghezza degli identificatori. Questo dovrebbe essere un numero molto piccolo, come 2-50 o meno.

Risolvendo questo problema, la probabilità di collisione tra identificatori di 500k di 15 caratteri è di circa 2 -52 , il che è probabilmente meno probabile degli errori non rilevati dai raggi cosmici, ecc.

Confronto con gli UUID

Secondo le loro specifiche, gli UUID non sono progettati per essere imprevedibili e non dovrebbero essere usati come identificatori di sessione.

Gli UUID nel loro formato standard occupano molto spazio: 36 caratteri per solo 122 bit di entropia. (Non tutti i bit di un UUID "casuale" sono selezionati in modo casuale.) Una stringa alfanumerica scelta casualmente racchiude più entropia in soli 21 caratteri.

Gli UUID non sono flessibili; hanno una struttura e un layout standardizzati. Questa è la loro principale virtù, nonché la loro principale debolezza. Quando si collabora con una parte esterna, la standardizzazione offerta dagli UUID può essere utile. Per un uso puramente interno, possono essere inefficienti.


6
Se hai bisogno di spazi nei tuoi, puoi attaccare .replaceAll("\\d", " ");alla fine della return new BigInteger(130, random).toString(32);linea per fare uno scambio regex. Sostituisce tutte le cifre con spazi. Funziona benissimo per me: lo sto usando come sostituto di un front-end Lorem Ipsum
weisjohn

4
@weisjohn Questa è una buona idea. Puoi fare qualcosa di simile con il secondo metodo, rimuovendo le cifre symbolse usando invece uno spazio; puoi controllare la lunghezza media della "parola" modificando il numero di spazi nei simboli (più occorrenze per parole più brevi). Per una soluzione di testo falso davvero esagerata, puoi usare una catena Markov!
Erickson,

4
Questi identificatori vengono selezionati casualmente dallo spazio di una determinata dimensione. Potrebbero essere lunghi 1 carattere. Se si desidera una lunghezza fissa, è possibile utilizzare la seconda soluzione, con SecureRandomun'istanza assegnata alla randomvariabile.
Erickson,

15
Perché .toString (32) anziché .toString (36)?
ejain

17
@ejain because 32 = 2 ^ 5; ogni carattere rappresenterà esattamente 5 bit e 130 bit possono essere equamente divisi in caratteri.
Erickson,

817

Java fornisce un modo per farlo direttamente. Se non vuoi i trattini, sono facili da eliminare. Basta usareuuid.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;
    }
}

Produzione:

uuid = 2d7428a6-b58c-4008-8575-f05549f16316

33
Attenzione che questa soluzione genera solo una stringa casuale con caratteri esadecimali. Che può andare bene in alcuni casi.
Dave,

5
La classe UUID è utile. Tuttavia, non sono compatti come gli identificatori prodotti dalle mie risposte. Questo può essere un problema, ad esempio, negli URL. Dipende dalle tue esigenze.
Erickson,

6
@Ruggs: l'obiettivo sono stringhe alfanumeriche. In che modo l'ampliamento dell'output a tutti i byte possibili si adatta a quello?
Erickson,

72
Secondo RFC4122 l'uso di UUID come token è una cattiva idea: non dare per scontato che gli UUID siano difficili da indovinare; non dovrebbero essere usati come capacità di sicurezza (identificatori il cui semplice possesso consente l'accesso), ad esempio. Una fonte di numeri casuali prevedibile aggraverà la situazione. ietf.org/rfc/rfc4122.txt
Somatik

34
UUID.randomUUID().toString().replaceAll("-", "");rende la stringa alfanumerica, come richiesto.
Numid

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, la soluzione più semplice qui per generare una stringa casuale di lunghezza specificata (a parte l'utilizzo di RandomStringUtils di Commons Lang).
Jonik,

12
Prendi in considerazione l'utilizzo al SecureRandomposto della Randomclasse. Se le password vengono generate su un server, potrebbe essere vulnerabile agli attacchi di temporizzazione.
foens

8
Vorrei anche aggiungere AB = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";lettere minuscole: e alcuni altri caratteri consentiti.
ACV,

1
Perché non inserire static Random rnd = new Random();il metodo?
Micro

4
@MicroR C'è una buona ragione per creare l' Randomoggetto in ogni chiamata di metodo? Io non la penso così.
cassiomolina,

484

Se sei felice di usare le classi Apache, puoi usare org.apache.commons.text.RandomStringGenerator(testo comune).

Esempio:

RandomStringGenerator randomStringGenerator =
        new RandomStringGenerator.Builder()
                .withinRange('0', 'z')
                .filteredBy(CharacterPredicates.LETTERS, CharacterPredicates.DIGITS)
                .build();
randomStringGenerator.generate(12); // toUpperCase() if you want

Poiché commons-lang 3.6, RandomStringUtilsè deprecato.


22
Ha appena esaminato la classe di Apache Commons Lang 3.3.1libreria menzionata e sta usando solo java.util.Randomper fornire sequenze casuali, quindi produce sequenze non sicure .
Yuriy Nakonechnyy,

16
Assicurati di utilizzare SecureRandom quando usi RandomStringUtils: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

NON USARE. Questo crea sequenze insicure !
Patrick Favre,

110

Puoi usare la libreria Apache per questo: RandomStringUtils

RandomStringUtils.randomAlphanumeric(20).toUpperCase();

18
@kamil, ho esaminato il codice sorgente per RandomStringUtils e utilizza un'istanza di java.util.Random istanziata senza argomenti. La documentazione per java.util.Random dice che utilizza l'ora di sistema corrente se non viene fornito alcun seme. Ciò significa che non può essere utilizzato per identificatori / chiavi di sessione poiché un utente malintenzionato può facilmente prevedere quali sono gli identificatori di sessione generati in un determinato momento.
Inshallah,

36
@Inshallah: stai (inutilmente) riprogettando il sistema. Anche se concordo sul fatto che utilizza il tempo come seed, l'attaccante deve avere accesso ai seguenti dati per ottenere effettivamente ciò che vuole 1. Tempo al millisecondo esatto, quando il codice è stato seedato 2. Numero di chiamate che si sono verificate finora 3. Atomicità per la propria chiamata (quindi quel numero di chiamate finora uguali) Se il tuo attaccante ha tutte e tre queste cose, allora hai un problema molto più grande a portata di mano ...
Ajeet Ganga

3
dipendenza dal grado: compile 'commons-lang:commons-lang:2.6'
younes0

4
@Ajeet questo non è vero. È possibile derivare lo stato del generatore di numeri casuali dal suo output. Se un utente malintenzionato può generare alcune migliaia di chiamate per generare token API casuali, l'utente malintenzionato sarà in grado di prevedere tutti i token API futuri.
Thomas Grainger,

3
@AjeetGanga Niente a che vedere con l'ingegneria. Se si desidera creare ID di sessione, è necessario un generatore pseudo casuale crittografico. Ogni prng che usa il tempo come seme è prevedibile e molto insicuro per i dati che dovrebbero essere imprevedibili. Basta usare SecureRandome sei bravo.
Patrick Favre,

105

In una riga:

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

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


9
Ma solo 6 lettere :(
Moshe Revah,

2
Mi ha aiutato anche me, ma solo cifre esadecimali :(
noquery

@Zippoxer, potresti concederlo diverse volte =)
daniel.bavrin

7
L'esempio del PO ha mostrato la seguente stringa come esempio AEYGF7K0DM1Xche non è esadecimale. Mi preoccupa quanto spesso le persone confondano alfanumerico con esadecimale. Non sono la stessa cosa.
hfontanez,

6
Questo è molto meno casuale di quanto dovrebbe essere data la lunghezza della stringa poiché Math.random()produce doubletra 0 e 1, quindi la parte esponente è per lo più inutilizzata. Utilizzare random.nextLongper un casuale longinvece di questo brutto hack.
maaartinus,

80

Ciò è facilmente realizzabile senza librerie esterne.

1. Generazione di dati pseudo casuali crittografici

Per prima cosa hai bisogno di un PRNG crittografico. Java ha SecureRandomquesto e di solito utilizza la migliore fonte di entropia sulla macchina (ad es /dev/random.). Leggi di più qui.

SecureRandom rnd = new SecureRandom();
byte[] token = new byte[byteLength];
rnd.nextBytes(token);

Nota: SecureRandom è il modo più lento, ma più sicuro in Java di generare byte casuali. Consiglio comunque di NON considerare le prestazioni qui poiché di solito non ha alcun impatto reale sulla tua applicazione a meno che tu non debba generare milioni di token al secondo.

2. Spazio richiesto di valori possibili

Successivamente devi decidere "quanto unico" deve essere il tuo token. L'unico e unico punto di considerare l'entropia è assicurarsi che il sistema sia in grado di resistere agli attacchi di forza bruta: lo spazio dei possibili valori deve essere così grande che qualsiasi attaccante potrebbe solo provare una percentuale trascurabile dei valori in un tempo non ridicolo 1 . Identificatori univoci come random UUIDhanno 122 bit di entropia (cioè 2 ^ 122 = 5.3x10 ^ 36) - la possibilità di collisione è "* (...) perché ci sia una possibilità su due miliardi di duplicazioni, 103 trilioni di versione 4 UUID devono essere generati 2 ". Sceglieremo 128 bit poiché si adatta esattamente a 16 byte ed è considerato altamente sufficienteper essere unico praticamente per tutti, ma i casi d'uso più estremi, e non devi pensare ai duplicati. Ecco una semplice tabella comparativa di entropia che include una semplice analisi del problema del compleanno .

confronto delle dimensioni dei token

Per requisiti semplici una lunghezza di 8 o 12 byte potrebbe essere sufficiente, ma con 16 byte sei sul "lato sicuro".

E questo è tutto. L'ultima cosa è pensare alla codifica in modo che possa essere rappresentata come un testo stampabile (leggi, aString ).

3. Codifica da binario a testo

Le codifiche tipiche includono:

  • Base64ogni personaggio codifica a 6 bit creando un sovraccarico del 33%. Fortunatamente ci sono implementazioni standard in Java 8+ e Android . Con Java precedente è possibile utilizzare una delle numerose librerie di terze parti . Se vuoi che i tuoi token siano url sicuri usa la versione url-sicura di RFC4648 (che di solito è supportata dalla maggior parte delle implementazioni). Esempio di codifica di 16 byte con riempimento:XfJhfv3C0P6ag7y9VQxSbw==

  • Base32ogni personaggio codifica a 5 bit creando un overhead del 40%. Questo userà A-Ze 2-7renderà ragionevolmente efficiente lo spazio pur essendo alfanumerico insensibile al maiuscolo / minuscolo. Non esiste un'implementazione standard nel JDK . Esempio di codifica di 16 byte senza riempimento:WUPIL5DQTZGMF4D3NX5L7LNFOY

  • Base16(esadecimale) ogni carattere codifica 4 bit richiedendo 2 caratteri per byte (ovvero 16 byte creano una stringa di lunghezza 32). Pertanto hex è meno efficiente in termini di spazio di Base32ma è sicuro da usare nella maggior parte dei casi (url) poiché utilizza solo 0-9e Ato F. Esempio codifica 16 byte: 4fa3dd0f57cb3bf331441ed285b27735. Vedi una discussione SO sulla conversione in esadecimale qui.

Codifiche aggiuntive come Base85 e l'esotico Base122 esistono con una migliore efficienza peggio / spazio. Puoi creare la tua codifica (che sostanzialmente fa la maggior parte delle risposte in questo thread) ma ti sconsiglio, se non hai requisiti molto specifici. Vedi altri schemi di codifica nell'articolo di Wikipedia.

4. Riepilogo ed esempio

  • Uso SecureRandom
  • Utilizzare almeno 16 byte (2 ^ 128) di possibili valori
  • Codifica in base alle tue esigenze (di solito hexo base32se ne hai bisogno per essere alfanumerico)

non

  • ... usa la tua codifica home brew: meglio gestibile e leggibile per gli altri se vedono quale codifica standard usi invece che strana per i loop che creano caratteri alla volta.
  • ... usa UUID: non ha garanzie sulla casualità; stai sprecando 6 bit di entropia e hai una rappresentazione di stringhe dettagliata

Esempio: generatore di token esadecimali

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

Esempio: generatore di token Base64 (Url sicuro)

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

Esempio: Java CLI Tool

Se vuoi uno strumento cli pronto all'uso puoi usare i dadi: https://github.com/patrickfav/dice

Esempio: problema correlato: proteggi i tuoi ID attuali

Se hai già un ID che puoi utilizzare (ad esempio un sintetico longnella tua entità), ma non vuoi pubblicare il valore interno , puoi utilizzare questa libreria per crittografarlo e offuscarlo: https://github.com/patrickfav / id-maschera

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

3
Questa risposta è completa e funziona senza aggiungere alcuna dipendenza. Se si desidera evitare possibili segni meno nell'output, è possibile evitare BigIntegers negativi utilizzando un parametro costruttore: BigInteger(1, token)anziché BigInteger(token).
francoisr,

Tanks @francoisr per il suggerimento, ho modificato l'esempio di codice
Patrick Favre, l'

import java.security.SecureRandom;e import java.math.BigInteger;sono necessari per far funzionare l'esempio, ma funziona benissimo!
altro

Una buona risposta ma / dev / random è un metodo di blocco che è la ragione per cui è lento al punto di bloccare se l'entropia è troppo bassa. Il metodo migliore e non bloccante è / dev / urandom. Questo può essere configurato tramite <jre> /lib/security/java.security e impostare securerandom.source = file: / dev /./ urandom
Muzammil

@Muzammil Vedi tersesystems.com/blog/2015/12/17/… (anche collegato nella risposta) - new SecureRandom()utilizza/dev/urandom
Patrick Favre

42

l'utilizzo di Dollar dovrebbe essere semplice come:

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

produce qualcosa del genere:

DKL1SBH9UJWC
JH7P0IT21EA5
5DTI72EO6SFU
HQUMJTEBNF7Y
1HCR6SKYWGT7

è possibile utilizzare SecureRandom con shuffle?
iwein

34

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

Ecco un esempio:

scala> RandomAlphaNum.gen(42)
res3: java.lang.String = uja6snx21bswf9t89s00bxssu8g6qlu16ffzqaxxoy

4
Ciò produrrà sequenze insicure, cioè sequenze che possono essere facilmente indovinate.
Yuriy Nakonechnyy,

8
Tutta questa generazione di int casuali a doppia infestazione è interrotta dal design, lenta e illeggibile. Usa Random#nextInto nextLong. Passare a SecureRandomse necessario.
maaartinus,

31

Nessuno qui sorprendente lo ha suggerito ma:

import java.util.UUID

UUID.randomUUID().toString();

Facile.

Il vantaggio di questo è che gli UUID sono belli e lunghi e sono quasi impossibili da scontrarsi.

Wikipedia ne ha una buona spiegazione:

"... solo dopo aver generato 1 miliardo di UUID al secondo per i prossimi 100 anni, la probabilità di creare un solo duplicato sarebbe di circa il 50%."

http://en.wikipedia.org/wiki/Universally_unique_identifier#Random_UUID_probability_of_duplicates

I primi 4 bit sono il tipo di versione e 2 per la variante in modo da ottenere 122 bit di casuale. Quindi, se vuoi , puoi troncare dalla fine per ridurre le dimensioni dell'UUID. Non è raccomandato ma hai ancora un sacco di casualità, abbastanza per i tuoi record da 500k facili.


39
Qualcuno l'ha suggerito, circa un anno prima di te.
Erickson,

31

Una soluzione semplice e breve, ma utilizza solo caratteri minuscoli e numerici:

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

La dimensione è di circa 12 cifre per la base 36 e non può essere ulteriormente migliorata in questo modo. Ovviamente puoi aggiungere più istanze.


11
Tieni presente che esiste una probabilità del 50% di un segno meno davanti al risultato! Quindi, avvolgendo r.nextLong () in un Math.abs () può essere usato, se non si desidera il segno meno: Long.toString(Math.abs(r.nextLong()), 36);
Ray Hulha,

5
@RayHulha: Se non vuoi il segno meno, dovresti tagliarlo, perché, sorprendentemente, Math.abs restituisce un valore negativo per Long.MIN_VALUE.
utente sconosciuto

Interessante il ritorno di Math.abs negativo. Altro qui: bmaurer.blogspot.co.nz/2006/10/…
Phil

1
Il problema con absviene risolto utilizzando un operatore bit a bit per cancellare il bit più significativo. Questo funzionerà per tutti i valori.
Radiodef,

1
@Radiodef Questo è essenzialmente ciò che ha detto @userunkown. Suppongo che anche tu potresti farlo << 1 >>> 1.
shmosel,

15

Un'alternativa in 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
È fantastico - ma se vuoi mantenerlo rigorosamente alfanumerico (0-9, az, AZ), vedi qui rationaljava.com/2015/06/…
Dan,

12

L'uso degli UUID è insicuro, perché parti dell'UUID non sono affatto casuali. La procedura di @erickson è molto accurata, ma non crea stringhe della stessa lunghezza. Il seguente frammento dovrebbe essere sufficiente:

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

Perché scegliere length*5. Supponiamo il semplice caso di una stringa casuale di lunghezza 1, quindi un carattere casuale. Per ottenere un carattere casuale contenente tutte le cifre 0-9 e i caratteri az, avremmo bisogno di un numero casuale compreso tra 0 e 35 per ottenere uno di ciascun carattere. BigIntegerfornisce un costruttore per generare un numero casuale, distribuito uniformemente sull'intervallo 0 to (2^numBits - 1). Sfortunatamente 35 non è un numero che può essere ricevuto da 2 ^ numBits - 1. Quindi abbiamo due opzioni: O andare con 2^5-1=31o 2^6-1=63. Se lo sceglieremmo 2^6otterremmo molti numeri "inutili" / "più lunghi". Pertanto 2^5è l'opzione migliore, anche se perdiamo 4 caratteri (wz). Per ora generare una stringa di una certa lunghezza, possiamo semplicemente usare a2^(length*numBits)-1numero. L'ultimo problema, se vogliamo una stringa con una certa lunghezza, random potrebbe generare un numero piccolo, quindi la lunghezza non viene rispettata, quindi dobbiamo riempire la stringa con la lunghezza richiesta anteporre gli zeri.


potresti spiegare meglio il 5?
Julian Suarez,

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

Quindi quello che fa è solo aggiungere la password nella stringa e ... sì, funziona bene, dai un'occhiata ... molto semplice. l'ho scritto io


Mi sono permesso di apportare alcune piccole modifiche. Perché lo aggiungi + 0spesso? Perché dividi la dichiarazione di spot e l'inizializzazione? Qual è il vantaggio degli indici 1,2,3,4 anziché 0,1,2,3? Ancora più importante: hai preso un valore casuale e confrontato con if-else 4 volte un nuovo valore, che potrebbe sempre non corrispondere, senza guadagnare più casualità. Ma sentiti libero di tornare indietro.
utente sconosciuto

8

Ho trovato questa soluzione che genera una stringa codificata esadecimale casuale. Il test unitario fornito sembra reggere il mio caso d'uso principale. Tuttavia, è leggermente più complesso di alcune delle altre risposte fornite.

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

Non credo sia giusto fallire per i token duplicati che sono puramente basati sulla probabilità.
Thom Wiggers,

8
  1. Cambia i caratteri String secondo le tue esigenze.

  2. La stringa è immutabile. Qui StringBuilder.appendè più efficiente della concatenazione di stringhe.


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
Questo non aggiunge nulla che le dozzine di risposte fornite in precedenza non coprivano. E la creazione di una nuova Randomistanza in ogni iterazione del ciclo è inefficiente.
Erickson,

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

Non mi piace davvero nessuna di queste risposte riguardo alla soluzione "semplice": S

Vorrei un semplice;), java puro, un liner (l'entropia si basa sulla lunghezza casuale della stringa e sul set di caratteri indicato):

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

o (un po 'più leggibile alla vecchia maniera)

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

Ma d'altra parte potresti anche andare con UUID che ha un'entropia abbastanza buona ( https://en.wikipedia.org/wiki/Universally_unique_identifier#Collisions ):

UUID.randomUUID().toString().replace("-", "")

Spero che aiuti.


6

Citi "semplice", ma nel caso in cui qualcun altro stia cercando qualcosa che soddisfi i requisiti di sicurezza più severi, potresti dare un'occhiata a jpwgen . jpwgen è modellato su pwgen in Unix ed è molto configurabile.


Grazie, risolto. Quindi almeno c'è fonte e il collegamento è valido. Il rovescio della medaglia, non sembra che sia stato aggiornato da un po ', anche se vedo che pwgen è stato aggiornato abbastanza di recente.
michaelok,

4

Puoi usare la classe UUID con il suo messaggio getLeastSignificantBits () per ottenere 64 bit di dati casuali, quindi convertirlo in un numero radix 36 (cioè una stringa composta da 0-9, AZ):

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

Questo produce una stringa lunga fino a 13 caratteri. Usiamo Math.abs () per assicurarci che non ci sia un segno meno all'interno.


2
Perché nel mondo useresti UUID per ottenere bit casuali? Perché non usare solo random.nextLong()? O addirittura Double.doubleToLongBits(Math.random())?
Erickson,

4

Puoi utilizzare il seguente codice, se la tua password obbligatoria contiene numeri speciali alfabetici:

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

Ecco il codice a una riga di AbacusUtil

String.valueOf(CharStream.random('0', 'z').filter(c -> N.isLetterOrDigit(c)).limit(12).toArray())

Casuale non significa che deve essere unico. per ottenere stringhe uniche, usando:

N.uuid() // e.g.: "e812e749-cf4c-4959-8ee1-57829a69a80f". length is 36.
N.guid() // e.g.: "0678ce04e18945559ba82ddeccaabfcd". length is 32 without '-'

3

Ecco una soluzione Scala:

(for (i <- 0 until rnd.nextInt(64)) yield { 
  ('0' + rnd.nextInt(64)).asInstanceOf[Char] 
}) mkString("")


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
Tale concatenazione di stringhe è inutilmente inefficiente. E il pazzo rientro rende il tuo codice quasi illeggibile. Questo è lo stesso dell'idea di Jamie, ma mal eseguita.
Erickson,

3

Penso che questa sia la soluzione più piccola qui, o quasi una delle più piccole:

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

Il codice funziona bene. Se stai usando questo metodo, ti consiglio di usare più di 10 caratteri. La collisione avviene a 5 caratteri / 30362 iterazioni. Ci sono voluti 9 secondi.


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
Veramente bello! Ma dovrebbe essere lengthinvece chars.lengthnel ciclo for: for (int i = 0; i < length; i++)
Inceneritore

2
public static String getRandomString(int length) 
{
   String randomStr = UUID.randomUUID().toString();
   while(randomStr.length() < length) {
       randomStr += UUID.randomUUID().toString();
   }
   return randomStr.substring(0, length);
}

3
È praticamente la stessa della risposta di Steve McLeod data due anni prima.
Erickson,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.