Jak wygenerować losowy ciąg alfa-numeryczny?

I've been looking for a simple Java algorithm to generate a pseudo-random alpha-numeric string. W mojej sytuacji byłby on używany jako unikalny identyfikator sesji / klucza, który "prawdopodobnie" byłby unikalny w ciągu 500K+ generacji (moje potrzeby nie'wymagają niczego bardziej wyrafinowanego).

Idealnie byłoby, gdybym mógł określić długość w zależności od moich potrzeb unikalności. Na przykład, wygenerowany ciąg o długości 12 może wyglądać coś jak "AEYGF7K0DM1X".

Rozwiązanie

Algorytm

Aby wygenerować losowy ciąg znaków, konkatenuj znaki losowo wybrane z zestawu dopuszczalnych symboli, aż ciąg osiągnie pożądaną długość.

Implementacja

Poniżej znajduje się dość prosty i bardzo elastyczny kod do generowania losowych identyfikatorów. Przeczytaj poniższe informacje, aby zapoznać się z ważnymi uwagami dotyczącymi zastosowania.

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

}

Przykłady użycia

Utwórz niezabezpieczony generator dla 8-znakowych identyfikatorów:

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

Utwórz bezpieczny generator dla identyfikatorów sesji:

RandomString session = new RandomString();

Utwórz generator z łatwymi do odczytania kodami do drukowania. Ciągi są dłuższe niż pełne ciągi alfanumeryczne, aby zrekompensować użycie mniejszej ilości symboli:

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

Użyj jako identyfikatorów sesji

Generowanie identyfikatorów sesji, które prawdopodobnie będą unikalne, nie jest wystarczająco dobre, lub możesz po prostu użyć prostego licznika. Atakujący porywają sesje, gdy używane są przewidywalne identyfikatory.

Istnieje napięcie pomiędzy długością a bezpieczeństwem. Krótsze identyfikatory są łatwiejsze do odgadnięcia, ponieważ jest mniej możliwości. Ale dłuższe identyfikatory zużywają więcej pamięci i pasma. Większy zestaw symboli pomaga, ale może powodować problemy z kodowaniem, jeśli identyfikatory są zawarte w adresach URL lub wpisywane ręcznie.

Podstawowym źródłem losowości, czyli entropii, dla identyfikatorów sesji powinien być generator liczb losowych przeznaczony do kryptografii. Jednak inicjalizacja tych generatorów może być czasami kosztowna obliczeniowo lub powolna, więc należy dołożyć starań, aby ponownie je wykorzystać, gdy jest to możliwe.

Użycie jako identyfikatorów obiektów

Nie każda aplikacja wymaga bezpieczeństwa. Losowe przydzielanie może być efektywnym sposobem dla wielu podmiotów do generowania identyfikatorów we wspólnej przestrzeni bez koordynacji lub podziału. Koordynacja może być powolna, szczególnie w środowisku klastrowym lub rozproszonym, a podział przestrzeni powoduje problemy, gdy podmioty kończą z udziałami, które są zbyt małe lub zbyt duże.

Identyfikatory generowane bez podjęcia środków, aby uczynić je nieprzewidywalnymi powinny być chronione innymi środkami, jeśli atakujący może być w stanie je przeglądać i nimi manipulować, jak to się dzieje w większości aplikacji internetowych. Powinien istnieć oddzielny system autoryzacji, który chroni obiekty, których identyfikator może być odgadnięty przez atakującego bez pozwolenia na dostęp.

Należy również zadbać o to, aby używać identyfikatorów, które są wystarczająco długie, aby kolizje były mało prawdopodobne, biorąc pod uwagę przewidywaną całkowitą liczbę identyfikatorów. Prawdopodobieństwo kolizji,]1 p, wynosi w przybliżeniu n2/(2qx), gdzie n to liczba faktycznie wygenerowanych identyfikatorów, q to liczba różnych symboli w alfabecie, a x to długość identyfikatorów. Powinna to być bardzo mała liczba, jak 2‑50 lub mniej.

Z powyższego wynika, że szansa na kolizję wśród 500k 15-znakowych identyfikatorów wynosi około 2‑52, co jest prawdopodobnie mniej prawdopodobne niż niewykryte błędy spowodowane promieniami kosmicznymi, itp.

Porównanie z identyfikatorami UUID

Zgodnie z ich specyfikacją, UUID nie są zaprojektowane do bycia nieprzewidywalnymi i nie powinny być używane jako identyfikatory sesji.

UUID w swoim standardowym formacie zajmują dużo miejsca: 36 znaków na tylko 122 bity entropii. (Nie wszystkie bity "random" UUID są wybierane losowo.) Losowo wybrany ciąg alfanumeryczny mieści więcej entropii w zaledwie 21 znakach.

UUID nie są elastyczne; mają znormalizowaną strukturę i układ. Jest to ich główna zaleta, jak również główna słabość. W przypadku współpracy z podmiotami zewnętrznymi, standaryzacja oferowana przez UUID może być pomocna. Do użytku czysto wewnętrznego, mogą być nieefektywne.

Komentarze (56)

Java dostarcza sposób na zrobienie tego bezpośrednio. Jeśli nie chcesz myślników, można je łatwo usunąć. Wystarczy użyć 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
Komentarze (14)

Tutaj jest to w Javie:

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

Oto przykładowy przebieg:

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