¿Cómo generar una cadena alfanumérica aleatoria?

1742

He estado buscando un algoritmo Java simple para generar una cadena alfanumérica pseudoaleatoria. En mi situación, se usaría como un identificador de sesión / clave único que "probablemente" sería único durante la 500K+generación (mis necesidades realmente no requieren nada mucho más sofisticado).

Idealmente, podría especificar una longitud dependiendo de mis necesidades únicas. Por ejemplo, una cadena generada de longitud 12 podría parecerse a algo así "AEYGF7K0DM1X".

Todd
fuente
150
Cuidado con la paradoja del cumpleaños .
pablosaraiva
58
Incluso teniendo en cuenta la paradoja del cumpleaños, si usa 12 caracteres alfanuméricos (62 en total), aún necesitaría más de 34 mil millones de cadenas para llegar a la paradoja. Y la paradoja del cumpleaños no garantiza una colisión de todos modos, solo dice que tiene más del 50% de posibilidades.
NullUserException
44
@NullUserException 50% de probabilidad de éxito (por intento) es muy alta: incluso con 10 intentos, la tasa de éxito es de 0,999. Con eso y el hecho de que puede probar MUCHO en un período de 24 horas en mente, no necesita 34 mil millones de cadenas para estar seguro de adivinar al menos una de ellas. Esa es la razón por la cual algunos tokens de sesión deberían ser muy, muy largos.
Pijusn
16
Supongo que estos 3 códigos de una sola línea son muy útiles ...Long.toHexString(Double.doubleToLongBits(Math.random())); UUID.randomUUID().toString(); RandomStringUtils.randomAlphanumeric(12);
Manindar
18
@Pijusn Sé que esto es viejo, pero ... la "probabilidad del 50%" en la paradoja del cumpleaños NO es "por intento", es una "probabilidad del 50% de que, de (en este caso) 34 mil millones de cadenas, exista en al menos un par de duplicados ". Necesitaría 1.6 sept illion - 1.6e21 - entradas en su base de datos para que haya un 50% de posibilidades por intento.
Tin Wizard

Respuestas:

1541

Algoritmo

Para generar una cadena aleatoria, concatene caracteres dibujados aleatoriamente del conjunto de símbolos aceptables hasta que la cadena alcance la longitud deseada.

Implementación

Aquí hay un código bastante simple y muy flexible para generar identificadores aleatorios. Lea la información que sigue para obtener notas importantes sobre la aplicación.

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

}

Ejemplos de uso

Cree un generador inseguro para identificadores de 8 caracteres:

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

Cree un generador seguro para identificadores de sesión:

RandomString session = new RandomString();

Cree un generador con códigos fáciles de leer para imprimir. Las cadenas son más largas que las cadenas alfanuméricas completas para compensar el uso de menos símbolos:

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

Usar como identificadores de sesión

Generar identificadores de sesión que probablemente sean únicos no es lo suficientemente bueno, o simplemente podría usar un contador simple. Los atacantes secuestran sesiones cuando se usan identificadores predecibles.

Hay tensión entre longitud y seguridad. Los identificadores más cortos son más fáciles de adivinar, porque hay menos posibilidades. Pero los identificadores más largos consumen más almacenamiento y ancho de banda. Un conjunto más grande de símbolos ayuda, pero puede causar problemas de codificación si los identificadores se incluyen en las URL o se vuelven a ingresar a mano.

La fuente subyacente de aleatoriedad, o entropía, para los identificadores de sesión debe provenir de un generador de números aleatorios diseñado para la criptografía. Sin embargo, la inicialización de estos generadores a veces puede ser computacionalmente costosa o lenta, por lo que se debe hacer un esfuerzo para reutilizarlos cuando sea posible.

Usar como identificadores de objeto

No todas las aplicaciones requieren seguridad. La asignación aleatoria puede ser una forma eficiente para que múltiples entidades generen identificadores en un espacio compartido sin ninguna coordinación o partición. La coordinación puede ser lenta, especialmente en un entorno agrupado o distribuido, y dividir un espacio causa problemas cuando las entidades terminan con recursos compartidos que son demasiado pequeños o demasiado grandes.

Los identificadores generados sin tomar medidas para hacerlos impredecibles deben protegerse por otros medios si un atacante puede verlos y manipularlos, como sucede en la mayoría de las aplicaciones web. Debe haber un sistema de autorización separado que proteja los objetos cuyo identificador pueda ser adivinado por un atacante sin permiso de acceso.

También se debe tener cuidado al usar identificadores que sean lo suficientemente largos como para hacer improbables las colisiones dado el número total anticipado de identificadores. Esto se conoce como "la paradoja del cumpleaños". La probabilidad de una colisión, p , es aproximadamente n 2 / (2q x ), donde n es el número de identificadores realmente generados, q es el número de símbolos distintos en el alfabeto yx es la longitud de los identificadores. Debe ser un número muy pequeño, como 2 a 50 o menos.

Resolver esto muestra que la posibilidad de colisión entre 500k identificadores de 15 caracteres es de 2 a 52 , lo que probablemente es menos probable que los errores no detectados de los rayos cósmicos, etc.

Comparación con UUID

Según su especificación, los UUID no están diseñados para ser impredecibles y no deben usarse como identificadores de sesión.

Los UUID en su formato estándar ocupan mucho espacio: 36 caracteres para solo 122 bits de entropía. (No todos los bits de un UUID "aleatorio" se seleccionan aleatoriamente). Una cadena alfanumérica elegida aleatoriamente contiene más entropía en solo 21 caracteres.

Los UUID no son flexibles; Tienen una estructura y diseño estandarizados. Esta es su principal virtud, así como su principal debilidad. Al colaborar con una parte externa, la estandarización ofrecida por UUID puede ser útil. Para uso puramente interno, pueden ser ineficientes.

erickson
fuente
66
Si necesita espacios en los suyos, puede agregar .replaceAll("\\d", " ");al final de la return new BigInteger(130, random).toString(32);línea para hacer un intercambio de expresiones regulares. Reemplaza todos los dígitos con espacios. Funciona muy bien para mí: estoy usando esto como un sustituto de un front-end Lorem Ipsum
weisjohn
44
@weisjohn Esa es una buena idea. Puede hacer algo similar con el segundo método, eliminando los dígitos symbolsy utilizando un espacio; puede controlar la longitud promedio de "palabras" cambiando la cantidad de espacios en los símbolos (más ocurrencias para palabras más cortas). Para una solución de texto falso realmente exagerada, ¡puedes usar una cadena de Markov!
erickson
44
Estos identificadores se seleccionan aleatoriamente desde un espacio de cierto tamaño. Podrían tener 1 carácter de largo. Si desea una longitud fija, puede usar la segunda solución, con una SecureRandominstancia asignada a la randomvariable.
erickson
15
¿Por qué .toString (32) en lugar de .toString (36)?
ejain
17
@ejain porque 32 = 2 ^ 5; cada carácter representará exactamente 5 bits, y 130 bits se pueden dividir equitativamente en caracteres.
erickson
817

Java proporciona una forma de hacer esto directamente. Si no quieres los guiones, son fáciles de quitar. Solo usauuid.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;
    }
}

Salida:

uuid = 2d7428a6-b58c-4008-8575-f05549f16316
Steve McLeod
fuente
33
Tenga en cuenta que esta solución solo genera una cadena aleatoria con caracteres hexadecimales. Lo que puede estar bien en algunos casos.
Dave
55
La clase UUID es útil. Sin embargo, no son tan compactos como los identificadores producidos por mis respuestas. Esto puede ser un problema, por ejemplo, en las URL. Depende de tus necesidades.
erickson
66
@Ruggs: el objetivo son las cadenas alfanuméricas. ¿Cómo encaja la ampliación de la salida a los posibles bytes?
erickson
72
De acuerdo con RFC4122, usar UUID como tokens es una mala idea: no asuma que los UUID son difíciles de adivinar; no deberían usarse como capacidades de seguridad (identificadores cuya mera posesión otorga acceso), por ejemplo. Una fuente de números aleatorios predecible exacerbará la situación. ietf.org/rfc/rfc4122.txt
Somatik
34
UUID.randomUUID().toString().replaceAll("-", "");hace que la cadena sea alfanumérica, según lo solicitado.
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();
}
máx.
fuente
61
+1, la solución más simple aquí para generar una cadena aleatoria de longitud especificada (además de usar RandomStringUtils de Commons Lang).
Jonik
12
Considere usar en SecureRandomlugar de la Randomclase. Si se generan contraseñas en un servidor, puede ser vulnerable a ataques de tiempo.
Foens
8
También agregaría minúsculas: AB = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";y algunos otros caracteres permitidos.
ACV
1
¿Por qué no poner static Random rnd = new Random();dentro del método?
Micro
44
@MicroR ¿Hay una buena razón para crear el Randomobjeto en cada invocación de método? No lo creo.
cassiomolin
484

Si está contento de usar las clases de Apache, puede usar org.apache.commons.text.RandomStringGenerator(commons-text).

Ejemplo:

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

Desde commons-lang 3.6, RandomStringUtilsestá en desuso.

numéro6
fuente
22
Acaba de examinar la clase de Apache Commons Lang 3.3.1biblioteca mencionada , y solo se usa java.util.Randompara proporcionar secuencias aleatorias, por lo que produce secuencias inseguras .
Yuriy Nakonechnyy
16
Asegúrese de usar SecureRandom cuando use RandomStringUtils: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
NO UTILICE. ¡Esto crea secuencias inseguras !
Patrick Favre
110

Puede usar la biblioteca Apache para esto: RandomStringUtils

RandomStringUtils.randomAlphanumeric(20).toUpperCase();
manish_s
fuente
18
@kamil, miré el código fuente de RandomStringUtils, y utiliza una instancia de java.util.Random instanciada sin argumentos. La documentación de java.util.Random dice que usa la hora actual del sistema si no se proporciona ninguna semilla. Esto significa que no puede usarse para identificadores / claves de sesión ya que un atacante puede predecir fácilmente cuáles son los identificadores de sesión generados en un momento dado.
Inshallah
36
@Inshallah: Estás (innecesariamente) sobreingeniería del sistema. Si bien estoy de acuerdo en que usa el tiempo como semilla, el atacante debe tener acceso a los siguientes datos para obtener realmente lo que quiere 1. Tiempo al milisegundo exacto, cuando se sembró el código 2. Número de llamadas que se han producido hasta ahora 3. Atomicidad para su propia llamada (de modo que el número de llamadas hasta ahora es igual a ramains) Si su atacante tiene estas tres cosas, entonces tiene un problema mucho mayor a mano ...
Ajeet Ganga
3
dependencia de gradle: compile 'commons-lang:commons-lang:2.6'
younes0
44
@Ajeet esto no es cierto. Puede derivar el estado del generador de números aleatorios a partir de su salida. Si un atacante puede generar unos pocos miles de llamadas para generar tokens API aleatorios, el atacante podrá predecir todos los tokens API futuros.
Thomas Grainger
3
@AjeetGanga Nada que ver con más de ingeniería. Si desea crear identificadores de sesión, necesita un generador pseudoaleatorio criptográfico. Cada prng que utiliza el tiempo como semilla es predecible y muy inseguro para datos que deberían ser impredecibles. Solo usa SecureRandomy eres bueno.
Patrick Favre
105

En una linea:

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

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

anónimo
fuente
99
Pero solo 6 letras :(
Moshe Revah
2
También me ayudó, pero solo dígitos hexadecimales :(
noquery
@Zippoxer, podrías concatenar eso varias veces =)
daniel.bavrin
77
El ejemplo del OP mostró la siguiente cadena como un ejemplo AEYGF7K0DM1Xque no es hexadecimal. Me preocupa con qué frecuencia la gente confunde alfanumérica con hexadecimal. No són la misma cosa.
hfontanez
66
Esto es mucho menos aleatorio de lo que debería asignarse a la longitud de la cadena, ya que Math.random()produce un valor doubleentre 0 y 1, por lo que la parte del exponente no se utiliza en su mayoría. Úselo random.nextLongpara un azar en longlugar de este truco feo.
maaartinus
80

Esto se puede lograr fácilmente sin bibliotecas externas.

1. Generación de datos pseudoaleatorios criptográficos

Primero necesitas un PRNG criptográfico. Java tiene SecureRandompara eso y generalmente usa la mejor fuente de entropía en la máquina (por ejemplo /dev/random). Leer más aquí.

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

Nota: SecureRandom es la forma más lenta pero más segura en Java de generar bytes aleatorios. Sin embargo, recomiendo NO considerar el rendimiento aquí, ya que generalmente no tiene un impacto real en su aplicación a menos que tenga que generar millones de tokens por segundo.

2. Espacio requerido de valores posibles

A continuación, debe decidir "qué tan único" debe ser su token. El único punto de considerar la entropía es asegurarse de que el sistema pueda resistir los ataques de fuerza bruta: el espacio de valores posibles debe ser tan grande que cualquier atacante solo pueda probar una proporción insignificante de los valores en un tiempo no ridículo 1 . Los identificadores únicos como aleatorio UUIDtienen 122 bits de entropía (es decir, 2 ^ 122 = 5.3x10 ^ 36): la posibilidad de colisión es "* (...) para que haya una posibilidad de duplicación de uno en mil millones, versión de 103 trillones Se deben generar 4 UUID de 2 ". Elegiremos 128 bits, ya que se ajusta exactamente a 16 bytes y se considera muy suficiente.por ser único para básicamente todos, pero los más extremos, use casos y no tenga que pensar en duplicados. Aquí hay una tabla de comparación simple de entropía que incluye un análisis simple del problema de cumpleaños .

comparación de tamaños de fichas

Para requisitos simples, 8 o 12 bytes de longitud pueden ser suficientes, pero con 16 bytes está en el "lado seguro".

Y eso es básicamente todo. Lo último es pensar en la codificación para que pueda representarse como un texto imprimible (leer, a String).

3. Codificación de binario a texto

Las codificaciones típicas incluyen:

  • Base64cada personaje codifica 6 bits creando una sobrecarga del 33%. Afortunadamente, hay implementaciones estándar en Java 8+ y Android . Con Java más antiguo, puede usar cualquiera de las numerosas bibliotecas de terceros . Si desea que sus tokens sean seguros para url, use la versión segura para url de RFC4648 (que generalmente es compatible con la mayoría de las implementaciones). Ejemplo de codificación de 16 bytes con relleno:XfJhfv3C0P6ag7y9VQxSbw==

  • Base32cada personaje codifica 5 bits creando una sobrecarga del 40%. Esto lo usará A-Zy 2-7lo hará razonablemente eficiente en cuanto al espacio, al tiempo que no distingue entre mayúsculas y minúsculas. No hay implementación estándar en el JDK . Ejemplo de codificación de 16 bytes sin relleno:WUPIL5DQTZGMF4D3NX5L7LNFOY

  • Base16(hexadecimal) cada carácter codifica 4 bits que requieren 2 caracteres por byte (es decir, 16 bytes crean una cadena de longitud 32). Por lo tanto, hex es menos eficiente que el espacio Base32pero es seguro de usar en la mayoría de los casos (url) ya que solo usa 0-9y Apara F. Ejemplo codifica 16 bytes: 4fa3dd0f57cb3bf331441ed285b27735. Vea una discusión SO sobre la conversión a hexadecimal aquí.

Existen codificaciones adicionales como Base85 y la exótica Base122 con mejor / peor eficiencia de espacio. Puede crear su propia codificación (que básicamente hacen la mayoría de las respuestas en este hilo) pero le aconsejaría que no lo haga, si no tiene requisitos muy específicos. Ver más esquemas de codificación en el artículo de Wikipedia.

4. Resumen y ejemplo

  • Utilizar SecureRandom
  • Use al menos 16 bytes (2 ^ 128) de valores posibles
  • Codifique de acuerdo con sus requisitos (generalmente hexo base32si necesita que sea alfanumérico)

No

  • ... use su codificación de preparación casera: mejor mantenible y legible para otros si ven qué codificación estándar usa en lugar de extraño para los bucles que crean caracteres a la vez.
  • ... use UUID: no tiene garantías de aleatoriedad; Está desperdiciando 6 bits de entropía y tiene una representación de cadena detallada

Ejemplo: generador de tokens hexadecimales

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

Ejemplo: Generador de tokens Base64 (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

Ejemplo: herramienta CLI de Java

Si desea una herramienta cli lista para usar, puede usar dados: https://github.com/patrickfav/dice

Ejemplo: Problema relacionado: proteja sus ID actuales

Si ya tiene una identificación que puede usar (por ejemplo, un sintético longen su entidad), pero no desea publicar el valor interno , puede usar esta biblioteca para cifrarlo y ofuscarlo: 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);
rev patrickf
fuente
3
Esta respuesta está completa y funciona sin agregar ninguna dependencia. Si desea evitar posibles signos menos en la salida, puede evitar BigIntegers negativos utilizando un parámetro constructor: en BigInteger(1, token)lugar de BigInteger(token).
francoisr
Tanks @francoisr para la pista, edité el ejemplo de código
Patrick Favre
import java.security.SecureRandom;y import java.math.BigInteger;son necesarios para que el ejemplo funcione, ¡pero funciona muy bien!
anothermh
Buena respuesta pero / dev / random es un método de bloqueo que es la razón por la que es lento hasta el punto de bloqueo si la entropía es demasiado baja. El mejor método y sin bloqueo es / dev / urandom. Esto se puede configurar a través de <jre> /lib/security/java.security y establecer securerandom.source = file: / dev /./ urandom
Muzammil
@Muzammil Ver tersesystems.com/blog/2015/12/17/… (también vinculado en la respuesta) - new SecureRandom()usos/dev/urandom
Patrick Favre
42

usar Dollar debería ser simple como:

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

produce algo como eso:

DKL1SBH9UJWC
JH7P0IT21EA5
5DTI72EO6SFU
HQUMJTEBNF7Y
1HCR6SKYWGT7
dfa
fuente
¿Es posible usar SecureRandom con shuffle?
iwein
34

Aquí está en 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();
  }
}

Aquí hay una muestra de ejecución:

scala> RandomAlphaNum.gen(42)
res3: java.lang.String = uja6snx21bswf9t89s00bxssu8g6qlu16ffzqaxxoy
Apocalipsis
fuente
44
Esto producirá secuencias inseguras, es decir, secuencias que se pueden adivinar fácilmente.
Yuriy Nakonechnyy
8
Toda esta generación int aleatoria infestada doblemente se rompe por diseño, lenta e ilegible. Use Random#nextInto nextLong. Cambie a SecureRandomsi es necesario.
maaartinus
31

Sorprendentemente, nadie aquí lo ha sugerido pero:

import java.util.UUID

UUID.randomUUID().toString();

Fácil.

El beneficio de esto es que los UUID son agradables y largos y se garantiza que sean casi imposibles de colisionar.

Wikipedia tiene una buena explicación:

"... solo después de generar mil millones de UUID por segundo durante los próximos 100 años, la probabilidad de crear solo un duplicado sería de aproximadamente el 50%".

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

Los primeros 4 bits son el tipo de versión y 2 para la variante, por lo que obtienes 122 bits aleatorios. Entonces, si lo desea , puede truncar desde el final para reducir el tamaño del UUID. No se recomienda, pero aún tiene mucha aleatoriedad, suficiente para que sus 500k registros sean fáciles.

Michael Allen
fuente
39
Alguien lo sugirió, aproximadamente un año antes que tú.
erickson
31

Una solución corta y fácil, pero usa solo minúsculas y números:

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

El tamaño es de aproximadamente 12 dígitos a la base 36 y no se puede mejorar aún más de esa manera. Por supuesto, puede agregar varias instancias.

usuario desconocido
fuente
11
¡Solo tenga en cuenta que hay un 50% de posibilidades de que aparezca un signo menos delante del resultado! Por lo tanto, puede usarse envolver r.nextLong () en Math.abs (), si no desea el signo menos: Long.toString(Math.abs(r.nextLong()), 36);
Ray Hulha
55
@RayHulha: Si no desea el signo menos, debe cortarlo, porque, sorprendentemente, Math.abs devuelve un valor negativo para Long.MIN_VALUE.
usuario desconocido
Interesante el Math.abs regresando negativo. Más aquí: bmaurer.blogspot.co.nz/2006/10/…
Phil
1
El problema con absse resuelve utilizando un operador bit a bit para borrar el bit más significativo. Esto funcionará para todos los valores.
Radiodef
1
@Radiodef Eso es esencialmente lo que dijo @userunkown. Supongo que también podrías hacerlo << 1 >>> 1.
shmosel
15

Una alternativa en Java 8 es:

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
fuente
3
Eso es genial, pero si quieres mantenerlo estrictamente alfanumérico (0-9, az, AZ) mira aquí racionaljava.com/2015/06/…
Dan
12

El uso de UUID es inseguro, porque partes del UUID no son aleatorias en absoluto. El procedimiento de @erickson es muy ordenado, pero no crea cadenas de la misma longitud. El siguiente fragmento debería ser suficiente:

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

¿Por qué elegir length*5? Asumamos el caso simple de una cadena aleatoria de longitud 1, entonces un carácter aleatorio. Para obtener un carácter aleatorio que contenga todos los dígitos 0-9 y caracteres az, necesitaríamos un número aleatorio entre 0 y 35 para obtener uno de cada carácter. BigIntegerproporciona un constructor para generar un número aleatorio, distribuido uniformemente en el rango 0 to (2^numBits - 1). Lamentablemente, 35 no es un número que pueda recibirse por 2 ^ numBits - 1. Por lo tanto, tenemos dos opciones: ir con 2^5-1=31o 2^6-1=63. Si escogiéramos 2^6, obtendríamos muchos números "innecesarios" / "más largos". Por 2^5lo tanto, es la mejor opción, incluso si perdemos 4 caracteres (wz). Para generar ahora una cadena de cierta longitud, simplemente podemos usar un2^(length*numBits)-1número. El último problema, si queremos una cadena con una cierta longitud, aleatorio podría generar un número pequeño, por lo que la longitud no se cumple, por lo que tenemos que rellenar la cadena a los ceros que preceden la longitud requerida.

revs Kristian Kraljic
fuente
¿podrías explicar mejor los 5?
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
fuente
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);
    }
}

Entonces, lo que esto hace es agregar la contraseña a la cadena y ... sí, funciona bien, compruébalo ... muy simple. yo lo escribi

cmpbah
fuente
Me permití hacer algunas modificaciones menores. ¿Por qué añades + 0eso a menudo? ¿Por qué se divide la declaración de spot y la inicialización? ¿Cuál es la ventaja de los índices 1,2,3,4 en lugar de 0,1,2,3? Lo más importante: tomó un valor aleatorio y lo comparó con if-else 4 veces un nuevo valor, que siempre podría no coincidir, sin obtener más aleatoriedad. Pero siéntase libre de retroceder.
usuario desconocido
8

Encontré esta solución que genera una cadena codificada hexadecimal aleatoria. La prueba unitaria proporcionada parece cumplir con mi caso de uso principal. Aunque, es un poco más complejo que algunas de las otras respuestas proporcionadas.

/**
 * 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
fuente
No creo que sea justo fallar por tokens duplicados, que se basa únicamente en la probabilidad.
Thom Wiggers
8
  1. Cambie los caracteres de cadena según sus requisitos.

  2. La cadena es inmutable. Aquí StringBuilder.appendes más eficiente que la concatenación de cadenas.


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();
    }
juguetón
fuente
3
Esto no agrega nada que las docenas de respuestas dadas anteriormente no cubrieron. Y crear una nueva Randominstancia en cada iteración del bucle es ineficiente.
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
fuente
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
fuente
7

Realmente no me gusta ninguna de estas respuestas con respecto a la solución "simple": S

Iría por un simple;), Java puro, un trazador de líneas (la entropía se basa en la longitud de la cadena aleatoria y el conjunto de caracteres dado):

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

o (un poco más legible a la antigua)

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

Pero, por otro lado, también podría usar UUID, que tiene una entropía bastante buena ( https://en.wikipedia.org/wiki/Universally_unique_identifier#Collisions ):

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

Espero que ayude.

Patrik Bego
fuente
6

Menciona "simple", pero en caso de que alguien más esté buscando algo que cumpla con los requisitos de seguridad más estrictos, es posible que desee echar un vistazo a jpwgen . jpwgen está modelado a partir de pwgen en Unix, y es muy configurable.

michaelok
fuente
Gracias, lo arreglé. Por lo tanto, al menos hay una fuente y el enlace es válido. En el lado negativo, no parece que se haya actualizado en un tiempo, aunque veo que pwgen se ha actualizado bastante recientemente.
michaelok
4

Puede usar la clase UUID con su mensaje getLeastSignificantBits () para obtener 64 bits de datos aleatorios, luego convertirlo a un número de radix 36 (es decir, una cadena que consiste en 0-9, AZ):

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

Esto produce una cadena de hasta 13 caracteres de longitud. Usamos Math.abs () para asegurarnos de que no haya un signo menos a escondidas.

Neuhaus
fuente
2
¿Por qué en el mundo usarías UUID para obtener bits aleatorios? ¿Por qué no solo usar random.nextLong()? O incluso Double.doubleToLongBits(Math.random())?
erickson
4

Puede usar el siguiente código, si su contraseña obligatoria contiene números caracteres alfabéticos especiales:

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
fuente
4

Aquí está el código de una línea de AbacusUtil

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

Aleatorio no significa que deba ser único. para obtener cadenas únicas, usando:

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

Aquí hay una solución Scala:

(for (i <- 0 until rnd.nextInt(64)) yield { 
  ('0' + rnd.nextInt(64)).asInstanceOf[Char] 
}) mkString("")
Ugo Matrangolo
fuente
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
fuente
2
Esa concatenación de cadenas es innecesariamente ineficiente. Y la sangría loca hace que su código sea casi ilegible. Esta es la misma idea de Jamie, pero mal ejecutada.
erickson
3

Creo que esta es la solución más pequeña aquí, o casi una de las más pequeñas:

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

El código funciona bien. Si está utilizando este método, le recomiendo que use más de 10 caracteres. La colisión ocurre a 5 caracteres / 30362 iteraciones. Esto tomó 9 segundos.

FileInputStream
fuente
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
fuente
1
¡Muy agradable! Pero debería estar en lengthlugar de chars.lengthen el ciclo for: for (int i = 0; i < length; i++)
Incinerador
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
fuente
3
Esto es más o menos lo mismo que la respuesta de Steve McLeod dada dos años antes.
erickson