Wie erstelle ich eine zufällige alphanumerische Zeichenfolge?

1741

Ich habe nach einem einfachen gesucht Java-Algorithmus , um eine pseudozufällige alphanumerische Zeichenfolge zu generieren. In meiner Situation würde es als eindeutige Sitzungs- / Schlüsselkennung verwendet, die "wahrscheinlich" über die 500K+Generation hinweg eindeutig wäre (meine Anforderungen erfordern nicht wirklich viel ausgefeilteres).

Im Idealfall kann ich eine Länge angeben, die meinen Anforderungen an die Einzigartigkeit entspricht. Beispielsweise könnte eine generierte Zeichenfolge mit der Länge 12 ungefähr so ​​aussehen "AEYGF7K0DM1X".

Todd
quelle
150
Vorsicht vor dem Geburtstagsparadoxon .
Pablosaraiva
58
Selbst unter Berücksichtigung des Geburtstagsparadoxons würden Sie bei Verwendung von 12 alphanumerischen Zeichen (insgesamt 62) immer noch weit über 34 Milliarden Zeichenfolgen benötigen, um das Paradoxon zu erreichen. Und das Geburtstagsparadox garantiert sowieso keine Kollision, es sagt nur, dass es über 50% Chance ist.
NullUserException
4
@NullUserException 50% Erfolgschance (pro Versuch) ist verdammt hoch: Selbst bei 10 Versuchen beträgt die Erfolgsrate 0,999. Angesichts dessen und der Tatsache, dass Sie innerhalb von 24 Stunden VIEL ausprobieren können, benötigen Sie keine 34 Milliarden Zeichenfolgen, um sicher zu sein, dass Sie mindestens eine davon erraten. Das ist der Grund, warum einige Sitzungstoken wirklich sehr, sehr lang sein sollten.
Pijusn
16
Diese 3 einzeiligen Codes sind sehr nützlich, denke ich.Long.toHexString(Double.doubleToLongBits(Math.random())); UUID.randomUUID().toString(); RandomStringUtils.randomAlphanumeric(12);
Manindar
18
@Pijusn Ich weiß, dass dies alt ist, aber ... die "50% Chance" im Geburtstagsparadoxon ist NICHT "pro Versuch", es ist "50% Chance, dass von (in diesem Fall) 34 Milliarden Zeichenfolgen bei existiert mindestens ein Paar Duplikate ". Sie benötigen 1,6 sept illion - 1,6e21 - Einträge in Ihrer Datenbank, damit eine Chance von 50% pro Versuch besteht.
Tin Wizard

Antworten:

1541

Algorithmus

Um eine zufällige Zeichenfolge zu generieren, verketten Sie Zeichen, die zufällig aus dem Satz akzeptabler Symbole gezogen wurden, bis die Zeichenfolge die gewünschte Länge erreicht hat.

Implementierung

Hier ist ein ziemlich einfacher und sehr flexibler Code zum Generieren von zufälligen Bezeichnern. Lesen Sie die folgenden Informationen, um wichtige Anwendungshinweise zu erhalten.

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

}

Anwendungsbeispiele

Erstellen Sie einen unsicheren Generator für 8-stellige Bezeichner:

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

Erstellen Sie einen sicheren Generator für Sitzungskennungen:

RandomString session = new RandomString();

Erstellen Sie einen Generator mit einfach zu lesenden Codes zum Drucken. Die Zeichenfolgen sind länger als vollständige alphanumerische Zeichenfolgen, um die Verwendung weniger Symbole zu kompensieren:

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

Verwendung als Sitzungskennung

Das Generieren von Sitzungskennungen, die wahrscheinlich eindeutig sind, ist nicht gut genug, oder Sie können einfach einen einfachen Zähler verwenden. Angreifer entführen Sitzungen, wenn vorhersehbare Kennungen verwendet werden.

Es gibt Spannungen zwischen Länge und Sicherheit. Kürzere Kennungen sind leichter zu erraten, da weniger Möglichkeiten bestehen. Längere Kennungen verbrauchen jedoch mehr Speicher und Bandbreite. Ein größerer Satz von Symbolen hilft, kann jedoch zu Codierungsproblemen führen, wenn Bezeichner in URLs enthalten sind oder von Hand neu eingegeben werden.

Die zugrunde liegende Quelle für Zufälligkeit oder Entropie für Sitzungskennungen sollte ein Zufallszahlengenerator sein, der für die Kryptographie entwickelt wurde. Das Initialisieren dieser Generatoren kann jedoch manchmal rechenintensiv oder langsam sein. Daher sollten Anstrengungen unternommen werden, um sie nach Möglichkeit wiederzuverwenden.

Verwendung als Objektkennungen

Nicht jede Anwendung erfordert Sicherheit. Die zufällige Zuweisung kann eine effiziente Möglichkeit für mehrere Entitäten sein, Bezeichner in einem gemeinsam genutzten Bereich ohne Koordination oder Partitionierung zu generieren. Die Koordination kann langsam sein, insbesondere in einer Cluster- oder verteilten Umgebung, und die Aufteilung eines Bereichs verursacht Probleme, wenn Entitäten zu kleine oder zu große Freigaben erhalten.

Bezeichner, die generiert werden, ohne Maßnahmen zu ergreifen, um sie unvorhersehbar zu machen, sollten auf andere Weise geschützt werden, wenn ein Angreifer sie möglicherweise anzeigen und bearbeiten kann, wie dies in den meisten Webanwendungen der Fall ist. Es sollte ein separates Autorisierungssystem geben, das Objekte schützt, deren Kennung von einem Angreifer ohne Zugriffsberechtigung erraten werden kann.

Es muss auch darauf geachtet werden, Kennungen zu verwenden, die lang genug sind, um Kollisionen angesichts der erwarteten Gesamtzahl der Kennungen unwahrscheinlich zu machen. Dies wird als "Geburtstagsparadoxon" bezeichnet. Die Wahrscheinlichkeit einer Kollision p beträgt ungefähr n 2 / (2q x ), wobei n die Anzahl der tatsächlich erzeugten Bezeichner ist, q die Anzahl der unterschiedlichen Symbole im Alphabet ist und x die Länge der Bezeichner ist. Dies sollte eine sehr kleine Zahl sein, wie 2 - 50 oder weniger.

Arbeiten dieser heraus zeigt , dass die Wahrscheinlichkeit einer Kollision zwischen 500k 15 Zeichen Bezeichner ist etwa 2 -52 , was wahrscheinlich weniger wahrscheinlich ist als unentdeckte Fehler durch kosmische Strahlung usw.

Vergleich mit UUIDs

Entsprechend ihrer Spezifikation sind UUIDs nicht unvorhersehbar und sollten nicht als Sitzungskennungen verwendet werden.

UUIDs in ihrem Standardformat nehmen viel Platz ein: 36 Zeichen für nur 122 Entropiebits. (Nicht alle Bits einer "zufälligen" UUID werden zufällig ausgewählt.) Eine zufällig ausgewählte alphanumerische Zeichenfolge enthält mehr Entropie in nur 21 Zeichen.

UUIDs sind nicht flexibel. Sie haben eine standardisierte Struktur und ein standardisiertes Layout. Dies ist ihre Haupttugend sowie ihre Hauptschwäche. Bei der Zusammenarbeit mit einer externen Partei kann die von UUIDs angebotene Standardisierung hilfreich sein. Für den rein internen Gebrauch können sie ineffizient sein.

erickson
quelle
6
Wenn Sie Leerzeichen in Ihrem benötigen, können Sie am .replaceAll("\\d", " ");Ende der return new BigInteger(130, random).toString(32);Zeile einen Regex-Tausch durchführen. Es ersetzt alle Ziffern durch Leerzeichen. Funktioniert hervorragend für mich: Ich verwende dies als Ersatz für ein Front-End Lorem Ipsum
weisjohn
4
@weisjohn Das ist eine gute Idee. Mit der zweiten Methode können Sie etwas Ähnliches tun, indem Sie die Ziffern entfernen symbolsund stattdessen ein Leerzeichen verwenden. Sie können die durchschnittliche "Wort" -Länge steuern, indem Sie die Anzahl der Leerzeichen in Symbolen ändern (mehr Vorkommen für kürzere Wörter). Für eine wirklich übertriebene Lösung für gefälschten Text können Sie eine Markov-Kette verwenden!
Erickson
4
Diese Bezeichner werden zufällig aus einem Raum einer bestimmten Größe ausgewählt. Sie können 1 Zeichen lang sein. Wenn Sie eine feste Länge wünschen, können Sie die zweite Lösung verwenden, wobei SecureRandomder randomVariablen eine Instanz zugewiesen ist.
Erickson
15
Warum .toString (32) statt .toString (36)?
Ejain
17
@ejain weil 32 = 2 ^ 5; Jedes Zeichen repräsentiert genau 5 Bits und 130 Bits können gleichmäßig in Zeichen unterteilt werden.
Erickson
817

Java bietet eine Möglichkeit, dies direkt zu tun. Wenn Sie die Striche nicht möchten, können Sie sie leicht entfernen. Benutz einfachuuid.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;
    }
}

Ausgabe:

uuid = 2d7428a6-b58c-4008-8575-f05549f16316
Steve McLeod
quelle
33
Beachten Sie, dass diese Lösung nur eine zufällige Zeichenfolge mit hexadezimalen Zeichen generiert. Was in einigen Fällen in Ordnung sein kann.
Dave
5
Die UUID-Klasse ist nützlich. Sie sind jedoch nicht so kompakt wie die durch meine Antworten erzeugten Kennungen. Dies kann beispielsweise bei URLs ein Problem sein. Kommt auf deine Bedürfnisse an.
Erickson
6
@Ruggs - Das Ziel sind alphanumerische Zeichenfolgen. Wie passt die Erweiterung der Ausgabe auf mögliche Bytes dazu?
Erickson
72
Laut RFC4122 ist die Verwendung von UUIDs als Token eine schlechte Idee: Gehen Sie nicht davon aus, dass UUIDs schwer zu erraten sind. Sie sollten beispielsweise nicht als Sicherheitsfunktionen verwendet werden (Kennungen, deren bloßer Besitz den Zugriff gewährt). Eine vorhersehbare Zufallszahlenquelle wird die Situation verschärfen. ietf.org/rfc/rfc4122.txt
Somatik
34
UUID.randomUUID().toString().replaceAll("-", "");macht die Zeichenfolge wie gewünscht alphanumerisch.
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();
}
maxp
quelle
61
+1, die einfachste Lösung zum Generieren einer zufälligen Zeichenfolge mit der angegebenen Länge (abgesehen von der Verwendung von RandomStringUtils von Commons Lang).
Jonik
12
Erwägen Sie die Verwendung SecureRandomanstelle der RandomKlasse. Wenn Kennwörter auf einem Server generiert werden, ist dies möglicherweise anfällig für Timing-Angriffe.
Fohlen
8
Ich würde auch Kleinbuchstaben hinzufügen: AB = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";und einige andere erlaubte Zeichen.
ACV
1
Warum nicht static Random rnd = new Random();in die Methode einbauen?
Micro
4
@MicroR Gibt es einen guten Grund, das RandomObjekt bei jedem Methodenaufruf zu erstellen ? Das glaube ich nicht.
Cassiomolin
484

Wenn Sie gerne Apache-Klassen verwenden, können Sie org.apache.commons.text.RandomStringGenerator(Commons-Text) verwenden.

Beispiel:

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

Da commons-lang 3.6, RandomStringUtilsist veraltet.

numéro6
quelle
22
Hat gerade die erwähnte Klasse von Apache Commons Lang 3.3.1Bibliotheken durchgesehen - und es wird nur verwendet java.util.Random, um zufällige Sequenzen bereitzustellen, so dass es unsichere Sequenzen erzeugt .
Yuriy Nakonechnyy
16
Stellen Sie sicher, dass Sie SecureRandom verwenden, wenn Sie RandomStringUtils verwenden: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
VERWENDE NICHT. Dies schafft unsichere Sequenzen !
Patrick Favre
110

Sie können die Apache-Bibliothek dafür verwenden: RandomStringUtils

RandomStringUtils.randomAlphanumeric(20).toUpperCase();
manish_s
quelle
18
@kamil, ich habe mir den Quellcode für RandomStringUtils angesehen und er verwendet eine Instanz von java.util.Random, die ohne Argumente instanziiert wurde. In der Dokumentation zu java.util.Random wird angegeben, dass die aktuelle Systemzeit verwendet wird, wenn kein Startwert bereitgestellt wird. Dies bedeutet, dass es nicht für Sitzungskennungen / -schlüssel verwendet werden kann, da ein Angreifer leicht vorhersagen kann, wie die generierten Sitzungskennungen zu einem bestimmten Zeitpunkt lauten.
Inshallah
36
@Inshallah: Sie überarbeiten (unnötigerweise) das System. Obwohl ich damit einverstanden bin, dass Zeit als Startwert verwendet wird, muss der Angreifer Zugriff auf die folgenden Daten haben, um tatsächlich das zu erhalten, was er möchte. 1. Zeit bis zur genauen Millisekunde, als der Code gesetzt wurde. 2. Anzahl der bisher aufgetretenen Anrufe 3. Atomizität für seinen eigenen Anruf (so dass die Anzahl der Anrufe bisher gleich bleibt) Wenn Ihr Angreifer alle drei dieser Dinge hat, haben Sie ein viel größeres Problem zur Hand ...
Ajeet Ganga
3
Gradle-Abhängigkeit: compile 'commons-lang:commons-lang:2.6'
younes0
4
@Ajeet das ist nicht wahr. Sie können den Status des Zufallszahlengenerators aus seiner Ausgabe ableiten. Wenn ein Angreifer einige tausend Aufrufe generieren kann, um zufällige API-Token zu generieren, kann der Angreifer alle zukünftigen API-Token vorhersagen.
Thomas Grainger
3
@AjeetGanga Nichts mit Über-Engineering zu tun. Wenn Sie Sitzungs-IDs erstellen möchten, benötigen Sie einen kryptografischen Pseudozufallsgenerator. Jedes Programm, das Zeit als Startwert verwendet, ist vorhersehbar und für Daten, die nicht vorhersehbar sein sollten, sehr unsicher. Einfach benutzen SecureRandomund du bist gut.
Patrick Favre
105

In einer Zeile:

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

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

anonym
quelle
9
Aber nur 6 Buchstaben :(
Moshe Revah
2
Es hat mir auch geholfen, aber nur hexadezimale Ziffern :(
noquery
@ Zippoxer, das könnte man mehrmals
besprechen
7
Das Beispiel des OP zeigte den folgenden String als Beispiel, AEYGF7K0DM1Xdas nicht hexadezimal ist. Es macht mir Sorgen, wie oft Menschen alphanumerisch mit hexadezimal verwechseln. Sie sind nicht dasselbe.
Hfontanez
6
Das ist viel weniger zufällig als es die String - Länge gegeben werden soll , als Math.random()ein produziert doublezwischen 0 und 1, so dass der Exponenten Teil meist ungenutzt ist. Verwenden Sie random.nextLongfür einen zufälligen longanstelle dieses hässlichen Hacks.
Maaartinus
80

Dies ist ohne externe Bibliotheken leicht zu erreichen.

1. Kryptografische Pseudozufallsdatengenerierung

Zuerst benötigen Sie ein kryptografisches PRNG. Java hat SecureRandomdafür und verwendet normalerweise die beste Entropiequelle auf der Maschine (z /dev/random. B. ). Lesen Sie hier mehr.

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

Hinweis: SecureRandom Dies ist die langsamste, aber sicherste Methode in Java, um zufällige Bytes zu generieren. Ich empfehle jedoch, die Leistung hier NICHT zu berücksichtigen, da dies normalerweise keine wirklichen Auswirkungen auf Ihre Anwendung hat, es sei denn, Sie müssen Millionen von Token pro Sekunde generieren.

2. Erforderlicher Platz für mögliche Werte

Als nächstes müssen Sie entscheiden, "wie einzigartig" Ihr Token sein muss. Der ganze und einzige Punkt bei der Betrachtung der Entropie besteht darin, sicherzustellen, dass das System Brute-Force-Angriffen widerstehen kann: Der Raum möglicher Werte muss so groß sein, dass jeder Angreifer nur einen vernachlässigbaren Anteil der Werte in nicht lächerlicher Zeit versuchen kann 1 . Eindeutige Bezeichner wie zufällig UUIDhaben eine Entropie von 122 Bit (dh 2 ^ 122 = 5,3 x 10 ^ 36) - die Wahrscheinlichkeit einer Kollision ist "* (...), damit eine Eins-zu-eine-Milliarde-Chance auf Duplizierung besteht, eine Version von 103 Billionen 4 UUIDs müssen 2 " generiert werden . Wir werden 128 Bit wählen, da es genau in 16 Bytes passt und als gesehen wird sehr ausreichend angesehen wirdUm für praktisch alle, aber extremsten Anwendungsfälle einzigartig zu sein, müssen Sie nicht über Duplikate nachdenken. Hier ist eine einfache Vergleichstabelle der Entropie einschließlich einer einfachen Analyse des Geburtstagsproblems .

Vergleich der Tokengrößen

Für einfache Anforderungen mag eine Länge von 8 oder 12 Bytes ausreichen, aber mit 16 Bytes sind Sie auf der "sicheren Seite".

Und das war's auch schon. Als letztes müssen Sie über die Codierung nachdenken, damit sie als druckbarer Text dargestellt werden kann (lesen Sie, a String).

3. Binär-Text-Codierung

Typische Codierungen umfassen:

  • Base64Jedes Zeichen codiert 6 Bit und verursacht einen Overhead von 33%. Glücklicherweise gibt es Standardimplementierungen in Java 8+ und Android . Mit älterem Java können Sie eine der zahlreichen Bibliotheken von Drittanbietern verwenden . Wenn Sie möchten, dass Ihre Token url-sicher sind, verwenden Sie die url-sichere Version von RFC4648 (die normalerweise von den meisten Implementierungen unterstützt wird). Beispiel für die Codierung von 16 Bytes mit Auffüllung:XfJhfv3C0P6ag7y9VQxSbw==

  • Base32Jedes Zeichen codiert 5 Bit und verursacht einen Overhead von 40%. Dies wird es verwenden A-Zund 2-7es einigermaßen platzsparend machen, während die Groß- und Kleinschreibung nicht berücksichtigt wird. Es gibt keine Standardimplementierung im JDK . Beispiel für die Codierung von 16 Bytes ohne Auffüllen:WUPIL5DQTZGMF4D3NX5L7LNFOY

  • Base16(hex) Jedes Zeichen codiert 4 Bit, wobei 2 Zeichen pro Byte erforderlich sind (dh 16 Byte erzeugen eine Zeichenfolge mit der Länge 32). Daher ist hex weniger platzsparend als Base32, ist aber in den meisten Fällen sicher zu verwenden (URL), da es nur 0-9und Azu verwendet F. Beispiel für die Codierung von 16 Bytes : 4fa3dd0f57cb3bf331441ed285b27735. Eine SO-Diskussion zum Konvertieren in Hex finden Sie hier.

Zusätzliche Codierungen wie Base85 und die exotische Base122 existieren mit besserer / schlechterer Raumeffizienz . Sie können Ihre eigene Codierung erstellen (was im Grunde die meisten Antworten in diesem Thread tun), aber ich würde davon abraten, wenn Sie keine sehr spezifischen Anforderungen haben. Siehe mehr Kodierungsschemata in Wikipedia - Artikel.

4. Zusammenfassung und Beispiel

  • Verwenden SecureRandom
  • Verwenden Sie mindestens 16 Byte (2 ^ 128) möglicher Werte
  • Codieren Sie entsprechend Ihren Anforderungen (normalerweise hexoder base32wenn Sie es alphanumerisch benötigen)

Tu es nicht

  • ... verwenden Sie Ihre Home Brew-Codierung: Besser wartbar und für andere lesbar, wenn sie sehen, welche Standardcodierung Sie verwenden, anstatt seltsam für Schleifen, die jeweils Zeichen erstellen.
  • ... UUID verwenden: Es gibt keine Garantien für Zufälligkeit; Sie verschwenden 6 Bit Entropie und haben eine ausführliche Zeichenfolgendarstellung

Beispiel: Hex Token Generator

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

Beispiel: Base64 Token Generator (Url Safe)

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

Beispiel: Java CLI Tool

Wenn Sie ein gebrauchsfertiges CLI-Tool benötigen, können Sie Würfel verwenden: https://github.com/patrickfav/dice

Beispiel: Verwandte Probleme - Schützen Sie Ihre aktuellen IDs

Wenn Sie bereits eine ID haben, die Sie verwenden können (z. B. eine synthetische ID longin Ihrer Entität), den internen Wert jedoch nicht veröffentlichen möchten , können Sie diese Bibliothek verwenden, um sie zu verschlüsseln und zu verschleiern: https://github.com/patrickfav / id-mask

IdMask<Long> idMask = IdMasks.forLongIds(Config.builder(key).build());
String maskedId = idMask.mask(id);
//example: NPSBolhMyabUBdTyanrbqT8
long originalId = idMask.unmask(maskedId);
patrickf
quelle
3
Diese Antwort ist vollständig und funktioniert ohne Hinzufügen von Abhängigkeiten. Wenn Sie mögliche Minuszeichen in der Ausgabe vermeiden möchten, können Sie negative BigIntegers mithilfe eines Konstruktorparameters verhindern: BigInteger(1, token)anstelle von BigInteger(token).
Francoisr
Tanks @francoisr für den Hinweis, ich habe das Codebeispiel bearbeitet
Patrick Favre
import java.security.SecureRandom;und import java.math.BigInteger;werden benötigt, damit das Beispiel funktioniert, aber es funktioniert großartig!
Anothermh
Gute Antwort, aber / dev / random ist eine Blockierungsmethode, weshalb es langsam ist, bis es blockiert, wenn die Entropie zu niedrig ist. Die bessere und nicht blockierende Methode ist / dev / urandom. Dies kann über <jre> /lib/security/java.security konfiguriert und securerandom.source = file: / dev /./ urandom
Muzammil
@Muzammil Siehe tersesystems.com/blog/2015/12/17/… (auch in der Antwort verlinkt) - new SecureRandom()verwendet/dev/urandom
Patrick Favre
42

Die Verwendung von Dollar sollte einfach sein als:

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

es gibt so etwas aus:

DKL1SBH9UJWC
JH7P0IT21EA5
5DTI72EO6SFU
HQUMJTEBNF7Y
1HCR6SKYWGT7
dfa
quelle
Ist es möglich, SecureRandom mit Shuffle zu verwenden?
iwein
34

Hier ist es 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();
  }
}

Hier ist ein Probelauf:

scala> RandomAlphaNum.gen(42)
res3: java.lang.String = uja6snx21bswf9t89s00bxssu8g6qlu16ffzqaxxoy
Apocalisp
quelle
4
Dies erzeugt unsichere Sequenzen, dh Sequenzen, die leicht erraten werden können.
Yuriy Nakonechnyy
8
All diese doppelt befallene zufällige int-Generierung ist vom Design her unterbrochen, langsam und unlesbar. Verwenden Sie Random#nextIntoder nextLong. Wechseln Sie SecureRandombei Bedarf zu.
Maaartinus
31

Überraschenderweise hat es hier niemand vorgeschlagen, aber:

import java.util.UUID

UUID.randomUUID().toString();

Einfach.

Dies hat den Vorteil, dass UUIDs schön lang sind und garantiert kaum kollidieren können.

Wikipedia hat eine gute Erklärung dafür:

"... erst nachdem in den nächsten 100 Jahren 1 Milliarde UUIDs pro Sekunde generiert wurden, würde die Wahrscheinlichkeit, nur ein Duplikat zu erstellen, bei etwa 50% liegen."

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

Die ersten 4 Bits sind der Versionstyp und 2 für die Variante, sodass Sie 122 zufällige Bits erhalten. Wenn Sie möchten , können Sie vom Ende abschneiden, um die Größe der UUID zu verringern. Es wird nicht empfohlen, aber Sie haben immer noch eine Menge Zufälligkeiten, genug für Ihre 500.000 Datensätze.

Michael Allen
quelle
39
Jemand hat es vorgeschlagen, ungefähr ein Jahr vor Ihnen.
Erickson
31

Eine kurze und einfache Lösung, die jedoch nur Kleinbuchstaben und Zahlen verwendet:

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

Die Größe beträgt ungefähr 12 Stellen bis zur Basis 36 und kann auf diese Weise nicht weiter verbessert werden. Natürlich können Sie mehrere Instanzen anhängen.

Benutzer unbekannt
quelle
11
Denken Sie daran, dass die Wahrscheinlichkeit eines Minuszeichens vor dem Ergebnis bei 50% liegt! Wenn Sie das Minuszeichen nicht möchten, können Sie also r.nextLong () in ein Math.abs () einbinden: Long.toString(Math.abs(r.nextLong()), 36);
Ray Hulha
5
@ RayHulha: Wenn Sie das Minuszeichen nicht möchten, sollten Sie es abschneiden, da Math.abs überraschenderweise einen negativen Wert für Long.MIN_VALUE zurückgibt.
Benutzer unbekannt
Interessant, dass die Math.abs negativ zurückkehren. Mehr hier: bmaurer.blogspot.co.nz/2006/10/…
Phil
1
Das Problem mit abswird gelöst, indem ein bitweiser Operator verwendet wird, um das höchstwertige Bit zu löschen. Dies funktioniert für alle Werte.
Radiodef
1
@Radiodef Genau das hat @userunkown gesagt. Ich nehme an, Sie könnten es auch tun << 1 >>> 1.
Shmosel
15

Eine Alternative in Java 8 ist:

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();
}
Howard Lovatt
quelle
3
Das ist großartig - aber wenn Sie es streng alphanumerisch (0-9, az, AZ) halten möchten, sehen Sie hier rationaljava.com/2015/06/…
Dan
12

Die Verwendung von UUIDs ist unsicher, da Teile der UUID überhaupt nicht zufällig sind. Die Prozedur von @erickson ist sehr ordentlich, erstellt jedoch keine Zeichenfolgen gleicher Länge. Das folgende Snippet sollte ausreichen:

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

Warum wählen length*5. Nehmen wir den einfachen Fall einer zufälligen Zeichenfolge der Länge 1 an, also eines zufälligen Zeichens. Um ein zufälliges Zeichen zu erhalten, das alle Ziffern 0-9 und die Zeichen az enthält, benötigen wir eine Zufallszahl zwischen 0 und 35, um eines von jedem Zeichen zu erhalten. BigIntegerstellt einen Konstruktor zum Generieren einer Zufallszahl bereit, die gleichmäßig über die Bereichsnummer verteilt ist. Das letzte Problem: Wenn wir eine Zeichenfolge mit einer bestimmten Länge möchten, kann zufällig eine kleine Zahl generiert werden, sodass die Länge nicht eingehalten wird. Daher müssen wir die Zeichenfolge auf die erforderliche Länge vor Nullen auffüllen.0 to (2^numBits - 1) . Leider ist 35 keine Nummer, die von 2 ^ numBits - 1 empfangen werden kann. Wir haben also zwei Möglichkeiten: Entweder mit 2^5-1=31oder 2^6-1=63. Wenn wir uns entscheiden 2^6würden, würden wir viele "unnötige" / "längere" Zahlen bekommen. Daher 2^5ist die bessere Option, auch wenn wir 4 Zeichen (wz) verlieren. Um jetzt einen String einer bestimmten Länge zu generieren, können wir einfach a verwenden2^(length*numBits)-1

Kristian Kraljic
quelle
Könntest du die 5 besser erklären?
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;
}
Rina
quelle
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);
    }
}

Also fügt man einfach das Passwort in die Zeichenfolge ein und ... ja, funktioniert gut, check it out ... sehr einfach. ich schrieb es

cmpbah
quelle
Ich erlaubte mir einige kleinere Änderungen vorzunehmen. Warum fügst du + 0das oft hinzu? Warum teilen Sie die Deklaration von Spot und Initialisierung auf? Was ist der Vorteil der Indizes 1,2,3,4 anstelle von 0,1,2,3? Am wichtigsten: Sie haben einen zufälligen Wert genommen und mit dem 4-fachen eines neuen Wertes verglichen, der immer nicht übereinstimmen könnte, ohne mehr Zufälligkeit zu erlangen. Aber zögern Sie nicht, einen Rollback durchzuführen.
Benutzer unbekannt
8

Ich habe diese Lösung gefunden, die eine zufällige hexadezimal codierte Zeichenfolge generiert. Der bereitgestellte Unit-Test scheint meinem primären Anwendungsfall standzuhalten. Es ist jedoch etwas komplexer als einige der anderen Antworten.

/**
 * 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);
        }
    }
}
Todd
quelle
Ich denke nicht, dass es fair ist, für doppelte Token zu scheitern, was nur auf der Wahrscheinlichkeit basiert.
Thom Wiggers
8
  1. Ändern Sie die Zeichenfolgen gemäß Ihren Anforderungen.

  2. String ist unveränderlich. Hier StringBuilder.appendist effizienter als die Verkettung von Zeichenfolgen.


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();
    }
munter
quelle
3
Dies fügt nichts hinzu, was die Dutzende von Antworten, die zuvor gegeben wurden, nicht abdeckten. Das Erstellen einer neuen RandomInstanz in jeder Iteration der Schleife ist ineffizient.
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;
    }
}
Jameskittu
quelle
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);
        }
    }
}
Suganya
quelle
7

Mag keine dieser Antworten in Bezug auf "einfache" Lösung wirklich: S.

Ich würde mich für ein einfaches;), reines Java, einen Liner entscheiden (Entropie basiert auf zufälliger Stringlänge und dem angegebenen Zeichensatz):

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

oder (etwas lesbarer alter Weg)

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

Andererseits können Sie auch eine UUID verwenden, die eine ziemlich gute Entropie aufweist ( https://en.wikipedia.org/wiki/Universally_unique_identifier#Collisions ):

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

Ich hoffe, das hilft.

Patrik Bego
quelle
6

Sie erwähnen "einfach", aber nur für den Fall, dass jemand anderes nach etwas sucht, das strengere Sicherheitsanforderungen erfüllt, sollten Sie sich jpwgen ansehen . jpwgen ist in Unix nach pwgen modelliert und sehr konfigurierbar.

Michaelok
quelle
Danke, habe es behoben. Es gibt also zumindest eine Quelle und der Link ist gültig. Auf der anderen Seite sieht es nicht so aus, als ob es seit einiger Zeit aktualisiert wurde, obwohl ich sehe, dass pwgen vor relativ kurzer Zeit aktualisiert wurde.
Michaelok
4

Sie können die UUID-Klasse mit ihrer getLeastSignificantBits () -Nachricht verwenden, um 64-Bit-Zufallsdaten abzurufen und sie dann in eine Radix 36-Zahl (dh eine Zeichenfolge bestehend aus 0-9, AZ) zu konvertieren:

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

Dies ergibt einen String mit einer Länge von bis zu 13 Zeichen. Wir verwenden Math.abs (), um sicherzustellen, dass sich kein Minuszeichen einschleicht.

neuhaus
quelle
2
Warum in aller Welt würden Sie UUID verwenden, um zufällige Bits zu erhalten? Warum nicht einfach benutzen random.nextLong()? Oder sogar Double.doubleToLongBits(Math.random())?
Erickson
4

Sie können den folgenden Code verwenden, wenn Ihr Kennwort obligatorisch alphabetische Sonderzeichen enthält:

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

}
Prasobh.K
quelle
4

Hier ist der einzeilige Code von AbacusUtil

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

Zufällig bedeutet nicht, dass es eindeutig sein muss. um eindeutige Zeichenfolgen zu erhalten, verwenden Sie:

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

Hier ist es eine Scala-Lösung:

(for (i <- 0 until rnd.nextInt(64)) yield { 
  ('0' + rnd.nextInt(64)).asInstanceOf[Char] 
}) mkString("")
Ugo Matrangolo
quelle
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;
}
Duggu
quelle
2
Diese Verkettung von Zeichenfolgen ist unnötig ineffizient. Und die verrückte Einrückung macht Ihren Code fast unlesbar. Dies ist das gleiche wie Jamies Idee, aber schlecht ausgeführt.
Erickson
3

Ich denke, dies ist die kleinste Lösung oder fast eine der kleinsten:

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

Der Code funktioniert einwandfrei. Wenn Sie diese Methode verwenden, empfehle ich Ihnen, mehr als 10 Zeichen zu verwenden. Die Kollision erfolgt bei 5 Zeichen / 30362 Iterationen. Dies dauerte 9 Sekunden.

FileInputStream
quelle
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;
    }
Prasad Parab
quelle
1
Wirklich nett! Aber es sollte lengthstatt chars.lengthin der for-Schleife sein: for (int i = 0; i < length; i++)
Incinerator
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);
}
Vin Ferothas
quelle
3
Dies entspricht in etwa der Antwort von Steve McLeod, die zwei Jahre zuvor gegeben wurde.
Erickson