1975 Stimmen

Wie man eine zufällige alphanumerische Zeichenfolge erzeugt

Ich habe nach einem einfach Java-Algorithmus zur Erzeugung einer pseudo-zufälligen alphanumerischen Zeichenkette. In meinem Fall würde sie als eindeutiger Sitzungs-/Schlüsselbezeichner verwendet, der "wahrscheinlich" in folgenden Fällen eindeutig ist 500K+ Generation (meine Bedürfnisse erfordern nicht wirklich etwas viel Anspruchsvolleres).

Im Idealfall könnte ich eine Länge angeben, die meinen Bedürfnissen nach Einzigartigkeit entspricht. Eine generierte Zeichenkette der Länge 12 könnte zum Beispiel wie folgt aussehen "AEYGF7K0DM1X" .

166 Stimmen

64 Stimmen

Selbst wenn man das Geburtstagsparadoxon in Betracht zieht, bräuchte man bei Verwendung von 12 alphanumerischen Zeichen (62 insgesamt) immer noch weit über 34 Milliarden Zeichenfolgen, um das Paradoxon zu erreichen. Und das Geburtstagsparadoxon garantiert sowieso keine Kollision, es sagt nur, dass die Wahrscheinlichkeit über 50 % liegt.

6 Stimmen

@NullUserException 50 % Erfolgschance (pro Versuch) ist verdammt hoch: Selbst bei 10 Versuchen liegt die Erfolgsquote bei 0,999. Wenn man das und die Tatsache bedenkt, dass man innerhalb von 24 Stunden VIELE Versuche machen kann, braucht man keine 34 Milliarden Zeichenfolgen, um ziemlich sicher zu sein, dass man mindestens eine davon errät. Das ist der Grund, warum einige Sitzungs-Tokens sehr, sehr lang sein sollten.

114voto

In einer Zeile:

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

Fuente: Java - Erzeugung einer zufälligen Zeichenkette

2 Stimmen

Mir hat es auch geholfen, aber nur mit Hexadezimalziffern :(

0 Stimmen

@Zippoxer, du könntest das mehrmals konkaten =)

7 Stimmen

Das Beispiel des Auftraggebers zeigte den folgenden String als Beispiel AEYGF7K0DM1X was nicht hexadezimal ist. Es beunruhigt mich, wie oft alphanumerisch mit hexadezimal verwechselt wird. Das ist nicht dasselbe.

92voto

Patrick Favre Punkte 31611

Dies ist ohne externe Bibliotheken leicht zu bewerkstelligen.

1. Kryptographische Pseudo-Zufallsdatengenerierung (PRNG)

Zunächst benötigen Sie einen kryptografischen PRNG. Java hat SecureRandom und verwendet normalerweise die beste Entropiequelle auf dem Rechner (z. B. /dev/random ). Lesen Sie hier mehr .

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

Note : SecureRandom ist die langsamste, aber sicherste Methode in Java, um Zufallsbytes zu erzeugen. Ich empfehle jedoch no Die Leistung sollte hier nicht berücksichtigt werden, da sie normalerweise keine wirklichen Auswirkungen auf Ihre Anwendung hat, es sei denn, Sie müssen Millionen von Token pro Sekunde erzeugen.

2. Erforderlicher Raum der möglichen Werte

Als Nächstes müssen Sie entscheiden, "wie einzigartig" Ihr Token sein soll. Der Sinn und Zweck der Betrachtung der Entropie ist es, sicherzustellen, dass das System Brute-Force-Angriffen widerstehen kann: Der Raum möglicher Werte muss so groß sein, dass ein Angreifer nur einen vernachlässigbaren Anteil der Werte in nicht unvorstellbarer Zeit ausprobieren kann 1 .

Eindeutige Identifikatoren wie zufällige UUID haben 122 Bit Entropie (d. h. 2^122 = 5,3x10^36) - die Wahrscheinlichkeit einer Kollision ist "*(...) damit die Wahrscheinlichkeit einer Vervielfältigung 1:1 Milliarde beträgt, müssen 103 Billionen UUIDs der Version 4 erzeugt werden 2 ". Wir wählen 128 Bit, da dies genau in 16 Bytes passt. und wird gesehen als völlig ausreichend für die Eindeutigkeit in praktisch allen, außer den extremsten, Anwendungsfällen und man muss sich keine Gedanken über Duplikate machen. Hier ist eine einfache Vergleichstabelle der Entropie einschließlich einer einfachen Analyse der Geburtstagsproblem .

Comparison of token sizes

Für einfache Anforderungen können 8 oder 12 Byte Länge ausreichen, aber mit 16 Byte ist man auf der "sicheren Seite".

Und das war's im Grunde auch schon. Als Letztes muss man sich Gedanken über die Kodierung machen, damit der Text als druckbarer Text dargestellt werden kann (lesen Sie, ein String ).

3. Binär-zu-Text-Kodierung

Typische Kodierungen sind:

  • Base64 Jedes Zeichen wird mit 6 Bit kodiert, was einen Overhead von 33 % bedeutet. Glücklicherweise gibt es Standardimplementierungen in Java 8+ y Android . Mit älterem Java können Sie jede der zahlreiche Bibliotheken von Drittanbietern . Wenn Sie möchten, dass Ihre Token URL-sicher sind, verwenden Sie die URL-sicher Version von RFC4648 (die normalerweise von den meisten Implementierungen unterstützt wird). Beispiel für die Kodierung von 16 Bytes mit Auffüllung: XfJhfv3C0P6ag7y9VQxSbw==

  • Base32 Jedes Zeichen wird mit 5 Bit kodiert, was einen Overhead von 40 % bedeutet. Dies verwendet A-Z y 2-7 Dadurch ist sie relativ platzsparend und unterscheidet nicht zwischen Groß- und Kleinschreibung und ist alphanumerisch. Es gibt keine Standardimplementierung im JDK . Beispiel für die Codierung von 16 Bytes ohne Auffüllen: WUPIL5DQTZGMF4D3NX5L7LNFOY

  • Base16 (hexadezimal) kodiert jedes Zeichen vier Bit, was zwei Zeichen pro Byte erfordert (d.h. 16 Bytes ergeben eine Zeichenkette der Länge 32). Daher ist die Hexadezimalkodierung weniger platzsparend als Base32 aber es ist in den meisten Fällen sicher zu verwenden (URL), da es nur 0-9 y A a F . Beispiel Kodierung 16 Bytes: 4fa3dd0f57cb3bf331441ed285b27735 . Siehe eine Stack Overflow-Diskussion über die Konvertierung in Hexadezimal hier .

Zusätzliche Kodierungen wie Basis85 und das Exotische Basis122 mit besserer/schlechterer Flächeneffizienz existieren. Sie können Ihre eigene Kodierung erstellen (was im Grunde die meisten Antworten in diesem Thread tun), aber ich würde davon abraten, wenn Sie nicht sehr spezifische Anforderungen haben. Siehe weitere Kodierungsverfahren im Wikipedia-Artikel .

4. Zusammenfassung und Beispiel

  • Utilice SecureRandom
  • Mindestens 16 Bytes (2^128) möglicher Werte verwenden
  • Codieren Sie entsprechend Ihren Anforderungen (normalerweise hex o base32 wenn es alphanumerisch sein soll)

Nicht

  • ... verwenden Sie Ihre selbst gebraute Kodierung: _besser pflegbar und lesbar für andere, wenn sie sehen, welche Standardkodierung Sie verwenden, anstatt seltsame für Schleifen, in denen Zeichen auf einmal erstellt werden._
  • ... UUID verwenden: es gibt keine Garantien für die Zufälligkeit; Sie verschwenden 6 Bits Entropie und haben eine ausführliche String-Darstellung

Beispiel: Hexadezimaler 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); // Hexadecimal encoding
}

//generateRandomHexToken(16) -> 2189df7475e96aa3982dbeab266497cd

Beispiel: Base64-Token-Generator (URL-sicher)

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

Wenn Sie ein einsatzbereites CLI-Tool wünschen, können Sie Folgendes verwenden Würfel :

Beispiel: Verwandtes Thema - Schützen Sie Ihre aktuellen Ids

Wenn Sie bereits eine ID haben, die Sie verwenden können (z. B. eine synthetische long in Ihrer Entität), sondern den internen Wert nicht veröffentlichen wollen können Sie diese Bibliothek verwenden, um sie zu verschlüsseln und unkenntlich zu machen: 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);

3 Stimmen

Diese Antwort ist vollständig und funktioniert ohne Hinzufügen von Abhängigkeiten. Wenn Sie mögliche Minuszeichen in der Ausgabe vermeiden wollen, können Sie negative BigInteger s unter Verwendung eines Konstruktorparameters: BigInteger(1, token) anstelle von BigInteger(token) .

0 Stimmen

Danke @francoisr für den Hinweis, ich habe das Codebeispiel bearbeitet

0 Stimmen

import java.security.SecureRandom; y import java.math.BigInteger; sind erforderlich, damit das Beispiel funktioniert, aber es funktioniert hervorragend!

42voto

dfa Punkte 110809

Verwendung von Dollar sollte so einfach sein wie:

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

Die Ausgabe sieht in etwa so aus:

DKL1SBH9UJWC
JH7P0IT21EA5
5DTI72EO6SFU
HQUMJTEBNF7Y
1HCR6SKYWGT7

0 Stimmen

Ist es möglich, SecureRandom mit Shuffle zu verwenden?

36voto

Apocalisp Punkte 34088

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

4 Stimmen

Dies führt zu unsichere Abläufe d.h. Sequenzen, die leicht erraten werden können.

9 Stimmen

All diese doppelt verseuchten Zufallsgeneratoren sind vom Design her kaputt, langsam und unlesbar. Verwenden Sie Random#nextInt o nextLong . Wechseln zu SecureRandom falls erforderlich.

35voto

user unknown Punkte 33856

Eine kurze und einfache Lösung, die allerdings nur Kleinbuchstaben und Ziffern verwendet:

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

Die Größe beträgt etwa 12 Stellen zur Basis 36 und kann so nicht weiter verbessert werden. Natürlich können Sie mehrere Instanzen anhängen.

11 Stimmen

Denken Sie nur daran, dass die Wahrscheinlichkeit eines Minuszeichens vor dem Ergebnis 50 % beträgt! Wenn Sie das Minuszeichen nicht wollen, können Sie r.nextLong() in ein Math.abs() einbetten: Long.toString(Math.abs(r.nextLong()), 36);

5 Stimmen

@RayHulha: Wenn Sie das Minuszeichen nicht wollen, sollten Sie es abschneiden, denn überraschenderweise liefert Math.abs einen negativen Wert für Long.MIN_VALUE.

0 Stimmen

Interessant, dass Math.abs negativ ist. Mehr hier: bmaurer.blogspot.co.nz/2006/10/

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X