Hoe genereer je een willekeurige alfa-numerieke string?

Ik'heb gezocht naar een simpel Java algoritme om een pseudo-willekeurige alfa-numerieke string te genereren. In mijn situatie zou het worden gebruikt als een unieke sessie/sleutel-identifier die "waarschijnlijk" uniek zou zijn over 500K+ generatie (mijn behoeften vereisen niet'echt iets veel geavanceerder).

Idealiter zou ik in staat zijn om een lengte te specificeren, afhankelijk van mijn uniciteitsbehoeften. Bijvoorbeeld, een gegenereerde string van lengte 12 zou eruit kunnen zien als "AEYGF7K0DM1X".

Oplossing

Algoritme

Om een willekeurige tekenreeks te genereren, voeg tekens willekeurig uit de verzameling van aanvaardbare symbolen samen tot de tekenreeks de gewenste lengte heeft bereikt.

Uitvoering

Hier's wat vrij eenvoudige en zeer flexibele code voor het genereren van willekeurige identifiers. Lees de informatie die volgt voor belangrijke opmerkingen over de toepassing.

import java.security.SecureRandom;
import java.util.Locale;
import java.util.Objects;
import java.util.Random;

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

}

Gebruiksvoorbeelden

Maak een onveilige generator voor 8-karakter identifiers:

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

Maak een veilige generator voor sessie-identifiers:

RandomString session = new RandomString();

Maak een generator met gemakkelijk te lezen codes om af te drukken. De strings zijn langer dan alfanumerieke strings om te compenseren voor het gebruik van minder symbolen:

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

Gebruik als sessie-identifiers

Het genereren van sessie identifiers die waarschijnlijk uniek zijn is niet goed genoeg, of je zou gewoon een simpele teller kunnen gebruiken. Aanvallers kapen sessies wanneer voorspelbare identifiers worden gebruikt.

Er is spanning tussen lengte en veiligheid. Kortere identifiers zijn makkelijker te raden, omdat er minder mogelijkheden zijn. Maar langere identifiers verbruiken meer opslag en bandbreedte. Een grotere set symbolen helpt, maar kan coderingsproblemen veroorzaken als identifiers in URL's worden opgenomen of met de hand worden ingevoerd.

De onderliggende bron van willekeurigheid, of entropie, voor sessie-identifiers moet komen van een willekeurige getallengenerator die ontworpen is voor cryptografie. Het initialiseren van deze generatoren kan soms echter rekentechnisch duur of traag zijn, dus moet getracht worden ze waar mogelijk te hergebruiken.

Gebruik als objectidentifiers

Niet elke toepassing vereist beveiliging. Willekeurige toewijzing kan een efficiënte manier zijn voor meerdere entiteiten om identifiers te genereren in een gedeelde ruimte zonder enige coördinatie of partitionering. Coördinatie kan traag zijn, vooral in een geclusterde of gedistribueerde omgeving, en het opsplitsen van een ruimte veroorzaakt problemen wanneer entiteiten eindigen met aandelen die te klein of te groot zijn.

Identifiers die worden gegenereerd zonder maatregelen te nemen om ze onvoorspelbaar te maken, moeten met andere middelen worden beschermd als een aanvaller ze zou kunnen bekijken en manipuleren, zoals in de meeste webtoepassingen gebeurt. Er zou een afzonderlijk autorisatiesysteem moeten zijn dat objecten beschermt waarvan de identifier door een aanvaller kan worden geraden zonder toegangstoestemming.

Er moet ook voor worden gezorgd dat identifiers lang genoeg zijn om botsingen onwaarschijnlijk te maken gezien het verwachte totale aantal identifiers. Dit wordt aangeduid als "the birthday paradox." De waarschijnlijkheid van een botsing, p, is ongeveer n2/(2qx), waarbij n het aantal werkelijk gegenereerde identifiers is, q het aantal verschillende symbolen in het alfabet, en x de lengte van de identifiers. Dit moet een heel klein getal zijn, zoals 2‑50 of minder.

Hieruit blijkt dat de kans op botsingen tussen 500k 15-karakter identifiers ongeveer 2‑52 is, wat waarschijnlijk minder waarschijnlijk is dan onopgemerkte fouten door kosmische straling, enz.

Vergelijking met UUID's

Volgens hun specificatie zijn UUID's niet ontworpen om onvoorspelbaar te zijn, en moeten ze niet worden gebruikt als sessie-identifiers.

UUID's in hun standaard formaat nemen veel ruimte in: 36 karakters voor slechts 122 bits aan entropie. (Niet alle bits van een "random" UUID worden willekeurig gekozen.) Een willekeurig gekozen alfanumerieke string heeft meer entropie in slechts 21 karakters.

UUID's zijn niet flexibel; zij hebben een gestandaardiseerde structuur en opmaak. Dit is hun voornaamste deugd, maar ook hun grootste zwakte. Bij samenwerking met een externe partij kan de standaardisatie die UUID's bieden nuttig zijn. Voor zuiver intern gebruik kunnen zij inefficiënt zijn.

Commentaren (56)

Java levert een manier om dit direct te doen. Als je de streepjes niet wilt, zijn ze gemakkelijk weg te strepen. Gebruik gewoon uuid.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;
    }
}

Uitvoer:

uuid = 2d7428a6-b58c-4008-8575-f05549f16316
Commentaren (14)

Hier is het 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's een voorbeeld run:

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