Vraag Hoe een willekeurige alfanumerieke reeks te genereren?


Ik ben op zoek naar een eenvoudig Java-algoritme om een ​​pseudo-willekeurige alfanumerieke reeks te genereren. In mijn situatie zou het worden gebruikt als een unieke sessie- / sleutelidentificatie die "waarschijnlijk" uniek zou zijn in de generatie van meer dan 500K + (mijn behoeften vereisen niet echt iets dat veel geavanceerder is). Idealiter zou ik een lengte kunnen specificeren die afhankelijk is van mijn eigenheid. Een gegenereerde reeks van lengte 12 kan er bijvoorbeeld ongeveer zo uitzien "AEYGF7K0DM1X".


1458


oorsprong


antwoorden:


Algoritme

Als u een willekeurige tekenreeks wilt genereren, plaatst u de tekens willekeurig uit de reeks met acceptabele symbolen totdat de tekenreeks de gewenste lengte heeft bereikt.

Implementatie

Hier is een vrij eenvoudige en zeer flexibele code voor het genereren van willekeurige identificaties. Lees de informatie die volgt voor belangrijke opmerkingen over toepassingen.

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

}

Gebruik voorbeelden

Maak een onveilige generator voor identificaties van 8 tekens:

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

Maak een veilige generator voor sessie-id's:

RandomString session = new RandomString();

Maak een generator met gemakkelijk leesbare codes om te printen. De tekenreeksen zijn langer dan volledige alfanumerieke reeksen 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-identificaties

Het genereren van sessie-id's die waarschijnlijk uniek zijn, is niet goed genoeg, of u kunt gewoon een eenvoudige teller gebruiken. Aanvallers kapen sessies wanneer voorspelbare identifiers worden gebruikt.

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

De onderliggende bron van willekeurigheid of entropie voor sessie-identificaties moet afkomstig zijn van een generator voor willekeurige getallen die is ontworpen voor cryptografie. Het initialiseren van deze generators kan soms echter rekenkundig duur of traag zijn, dus moet er zoveel mogelijk moeite worden gedaan om ze te hergebruiken.

Gebruik als object-ID's

Niet elke applicatie vereist beveiliging. Willekeurige toewijzing kan een efficiënte manier zijn voor meerdere entiteiten om identifiers in een gedeelde ruimte te genereren zonder enige coördinatie of partitionering. De coördinatie kan langzaam 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 op andere manieren worden beschermd als een aanvaller in staat zou zijn om ze te bekijken en te manipuleren, zoals in de meeste webtoepassingen. Er moet een afzonderlijk machtigingssysteem zijn dat objecten beschermt waarvan de identifier door een aanvaller zonder toegangsrechten geraden kan worden.

Er moet ook voor worden gezorgd dat ID's worden gebruikt die lang genoeg zijn om botsingen onwaarschijnlijk te maken gezien het verwachte totale aantal ID's. Dit wordt de "verjaardagsparadox" genoemd. De kans op een botsing,  p, is ongeveer n2/ (2qX), waar n is het aantal daadwerkelijk gegenereerde ID's, q is het aantal verschillende symbolen in het alfabet, en X is de lengte van de ID's. Dit zou een heel klein aantal moeten zijn, zoals 2-50 of minder.

Uit dit onderzoek blijkt dat de kans op een botsing tussen 500 k-tekens met 15 tekens ongeveer 2 is-52, wat waarschijnlijk minder waarschijnlijk is dan onopgemerkte fouten van kosmische stralen, enz.

Vergelijking met UUID's

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

UUID's in hun standaardformaat nemen veel ruimte in beslag: 36 tekens voor slechts 122 bits van entropie. (Niet alle bits van een "willekeurige" UUID worden willekeurig geselecteerd.) Een willekeurig gekozen alfanumerieke tekenreeks verpakt meer entropie in slechts 21 tekens.

UUID's zijn niet flexibel; ze hebben een gestandaardiseerde structuur en lay-out. Dit is zowel hun belangrijkste deugd als hun belangrijkste zwakte. Wanneer u samenwerkt met een externe partij, kan de standaardisatie die wordt aangeboden door UUID's nuttig zijn. Voor puur intern gebruik kunnen ze inefficiënt zijn.


1396



Java biedt een manier om dit direct te doen. Als u de streepjes niet wilt, kunt u ze gemakkelijk verwijderen. 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;
    }
}

Output:

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

732



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

468



Als u graag Apache-klassen gebruikt, kunt u deze gebruiken org.apache.commons.text.RandomStringGenerator (Commons-tekst).

Voorbeeld:

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

Sinds commons-lang 3.6, RandomStringUtils is verouderd.


453



Op één regel:

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

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


90



U kunt hiervoor de Apache-bibliotheek gebruiken: RandomStringUtils

RandomStringUtils.randomAlphanumeric(20).toUpperCase();

87



gebruik makend van Dollar moet eenvoudig zijn 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));
    }
}

het levert zoiets op:

DKL1SBH9UJWC
JH7P0IT21EA5
5DTI72EO6SFU
HQUMJTEBNF7Y
1HCR6SKYWGT7

36



Dit is gemakkelijk te bereiken zonder externe bibliotheken.

1. Cryptografische pseudo-willekeurige gegevensvorming

Eerst heb je een cryptografische PRNG nodig. Java heeft SecureRandom daarvoor wordt meestal de beste entropiebron op de machine gebruikt (bijv. /dev/random). Lees hier meer.

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

Notitie:  SecureRandom is de langzaamste, maar meest veilige manier in Java voor het genereren van willekeurige bytes. Ik raad echter aan de prestaties hier NIET te overwegen, omdat dit doorgaans geen echte invloed heeft op je toepassing, tenzij je miljoenen tokens per seconde moet genereren.

2. Vereiste ruimte van mogelijke waarden

Vervolgens moet u beslissen "hoe uniek" uw token moet zijn. Het enige en enige punt van overweging van entropie is om ervoor te zorgen dat het systeem brute kracht aanvallen kan weerstaan: de ruimte van mogelijke waarden moet zo groot zijn dat een aanvaller slechts een verwaarloosbaar deel van de waarden in niet-idiote tijd zou kunnen proberen1. Unieke ID's zoals willekeurig UUID hebben 122bit entropie (dwz 2 ^ 122 = 5,3x10 ^ 36) - de kans op een botsing is "* (...) voor een kans van 1 miljard miljard op duplicatie, 103 biljoen versie 4 UUID's moet worden gegenereerd2". We zullen 128 bit kiezen omdat het precies in 16 bytes past en wordt gezien als hoog genoeg Omdat ze uniek zijn voor in principe alle, maar de meest extreme, gebruik je cases en hoef je niet na te denken over duplicaten. Hier is een eenvoudige vergelijkingstabel van entropie inclusief eenvoudige analyse van de verjaardags probleem.

comparison of token sizes

Voor eenvoudige vereisten is een lengte van 8 of 12 bytes voldoende, maar met 16 bytes bent u aan de "veilige kant".

En dat is het eigenlijk. Het laatste is om na te denken over codering, zodat het kan worden weergegeven als een afdrukbare tekst (lees, a String).

3. Binair naar tekstcodering

Typische coderingen zijn:

  • Base64 elk karakter codeert voor 6bit en genereert een overhead van 33%. Helaas is er geen standaardimplementatie in de JDK (7 en lager - er is in Android en Java 8+). Maar er zijn talloze bibliotheken die dit toevoegen. Het nadeel is die norm Base64 is niet veilig voor bijvoorbeeld urls en als bestandsnaam in de meeste bestandssystemen die extra codering vereisen (bijv. URL-codering) of de URL-veilige versie van Base64 wordt gebruikt. Voorbeeld codering van 16 bytes met opvulling: XfJhfv3C0P6ag7y9VQxSbw==

  • Base32 elk karakter codeert voor 5bit en creëert een overhead van 40%. Dit zal gebruiken A-Z en 2-7 waardoor het redelijk ruimtebesparend is terwijl het niet hoofdlettergevoelig is alfanumeriek. Er is geen standaardimplementatie in de JDK. Voorbeeld codering van 16 bytes zonder opvulling: WUPIL5DQTZGMF4D3NX5L7LNFOY

  • Base16 (hex) elk karakter codeert voor 4 bit en vereist 2 tekens per byte (dwz 16 bytes maken een reeks van lengte 32). Daarom is hex minder ruimtebesparend dan Base32 maar is veilig om te gebruiken in de meeste gevallen (url) omdat het alleen gebruikt 0-9 en A naar F. Voorbeeld codering van 16 bytes: 4fa3dd0f57cb3bf331441ed285b27735. Zie een SO-discussie over het converteren naar hexadecimaal hier.

Aanvullende coderingen zoals Base85 en het exotische Base122 bestaan ​​met een betere / slechtere ruimte-efficiëntie. Je kunt je eigen codering maken (wat eigenlijk de meeste antwoorden in deze thread zijn) maar ik zou het afraden, als je niet heel specifieke vereisten hebt. Zien meer coderingsschema's in het Wikipedia-artikel.

4. Samenvatting en voorbeeld

  • Gebruik SecureRandom
  • Gebruik ten minste 16 bytes (2 ^ 128) van mogelijke waarden
  • Codeer volgens uw vereisten (meestal hex of base32 als je het alfanumeriek wilt)

doe niet

  • ... gebruik uw thuisbrouwcodering: beter onderhoudbaar en leesbaar voor anderen als ze zien welke standaardcodering je gebruikt in plaats van raar voor loops die tegelijkertijd karakters maken.
  • ... gebruik UUID: je verspilt 6 bits entropie en hebt een uitgebreide stringvoorstelling

Voorbeeld: 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

Voorbeeld: Tool

Als je een kant-en-klare cli-tool wilt, kun je dobbelstenen gebruiken: https://github.com/patrickfav/dice


32



Verrassend niemand hier heeft het voorgesteld, maar:

import java.util.UUID

UUID.randomUUID().toString();

Gemakkelijk.

Voordeel hiervan is dat UUID's leuk en lang zijn en gegarandeerd bijna onmogelijk te botsen zijn.

Wikipedia heeft daar een goede verklaring voor:

"... alleen na het genereren van 1 miljard UUID's per seconde voor de komende 100 jaar, zou de kans op het maken van slechts één duplicaat ongeveer 50% zijn."

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

De eerste 4 bits zijn het versietype en 2 voor de variant, zodat u 122 willekeurige bits krijgt. Dus als je willen aan u kunt vanaf het einde afkappen om de UUID te verkleinen. Het wordt niet aanbevolen, maar je hebt nog steeds veel willekeur, genoeg voor je 500k-records eenvoudig.


29



Hier is het op 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 is een voorbeeldrun:

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

28



Een korte en eenvoudige oplossing, maar gebruikt alleen kleine letters en cijfers:

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

De grootte is ongeveer 12 cijfers naar basis 36 en kan niet verder worden verbeterd, op die manier. Natuurlijk kunt u meerdere instanties toevoegen.


21