Como gerar uma sequência alfanumérica aleatória?


1741

Eu estive procurando por um algoritmo Java simples para gerar uma seqüência alfanumérica pseudo-aleatória. Na minha situação, seria usado como um único identificador de sessão / chave que "provavelmente" seria único ao longo da 500K+geração (minhas necessidades realmente não exigem nada muito mais sofisticado).

Idealmente, eu seria capaz de especificar um comprimento, dependendo das minhas necessidades de exclusividade. Por exemplo, uma sequência gerada de comprimento 12 pode se parecer com "AEYGF7K0DM1X".



58
Mesmo levando em consideração o paradoxo do aniversário, se você usar 12 caracteres alfanuméricos (62 no total), ainda precisará de mais de 34 bilhões de strings para alcançar o paradoxo. E o paradoxo do aniversário não garante uma colisão, apenas diz que tem mais de 50% de chance.
NullUserException 29/10/12

4
@NullUserException A chance de sucesso de 50% (por tentativa) é muito alta: mesmo com 10 tentativas, a taxa de sucesso é de 0,999. Com isso e com o fato de que você pode tentar MUITO em um período de 24 horas em mente, você não precisa de 34 bilhões de strings para ter certeza de adivinhar pelo menos uma delas. Essa é a razão pela qual alguns tokens de sessão devem ser muito, muito longos.
Pijusn 31/01

16
Estes 3 códigos de linha única são muito úteis palpite i ..Long.toHexString(Double.doubleToLongBits(Math.random())); UUID.randomUUID().toString(); RandomStringUtils.randomAlphanumeric(12);
Manindar

18
@Pijusn Eu sei que isso é antigo, mas ... a "chance de 50%" no paradoxo do aniversário NÃO é "por tentativa", é "50% de chance de que, dentre (nesse caso) 34 bilhões de strings, exista em menos um par de duplicatas ". Você precisaria de entradas 1.6 sept illion - 1.6e21 - em seu banco de dados para que houvesse 50% de chance por tentativa.
Assistente de Tin

Respostas:


1541

Algoritmo

Para gerar uma sequência aleatória, concatene caracteres desenhados aleatoriamente a partir do conjunto de símbolos aceitáveis ​​até que a sequência atinja o comprimento desejado.

Implementação

Aqui está um código bastante simples e muito flexível para gerar identificadores aleatórios. Leia as informações a seguir para obter importantes notas de aplicação.

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

}

Exemplos de uso

Crie um gerador inseguro para identificadores de 8 caracteres:

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

Crie um gerador seguro para identificadores de sessão:

RandomString session = new RandomString();

Crie um gerador com códigos fáceis de ler para impressão. As cadeias são mais longas que as alfanuméricas completas para compensar o uso de menos símbolos:

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

Use como identificadores de sessão

Gerar identificadores de sessão que provavelmente são únicos não é bom o suficiente, ou você pode simplesmente usar um contador simples. Os invasores sequestram sessões quando identificadores previsíveis são usados.

Há tensão entre comprimento e segurança. Identificadores mais curtos são mais fáceis de adivinhar, porque há menos possibilidades. Mas identificadores mais longos consomem mais armazenamento e largura de banda. Um conjunto maior de símbolos ajuda, mas pode causar problemas de codificação se os identificadores forem incluídos nos URLs ou digitados novamente manualmente.

A fonte subjacente de aleatoriedade, ou entropia, para identificadores de sessão deve vir de um gerador de números aleatórios projetado para criptografia. No entanto, a inicialização desses geradores às vezes pode ser cara ou computacionalmente lenta; portanto, deve-se fazer um esforço para reutilizá-los quando possível.

Use como identificadores de objeto

Nem todo aplicativo requer segurança. A atribuição aleatória pode ser uma maneira eficiente de várias entidades gerarem identificadores em um espaço compartilhado sem nenhuma coordenação ou particionamento. A coordenação pode ser lenta, especialmente em um ambiente em cluster ou distribuído, e a divisão de um espaço causa problemas quando as entidades terminam com compartilhamentos muito pequenos ou grandes.

Os identificadores gerados sem tomar medidas para torná-los imprevisíveis devem ser protegidos por outros meios, se um invasor puder visualizá-los e manipulá-los, como acontece na maioria dos aplicativos da web. Deve haver um sistema de autorização separado que proteja objetos cujo identificador possa ser adivinhado por um invasor sem permissão de acesso.

Também é preciso ter cuidado para usar identificadores que sejam longos o suficiente para tornar improváveis ​​as colisões, dado o número total previsto de identificadores. Isso é conhecido como "o paradoxo do aniversário". A probabilidade de uma colisão, p , é aproximadamente n 2 / (2q x ), onde n é o número de identificadores realmente gerados, q é o número de símbolos distintos no alfabeto ex é o comprimento dos identificadores. Esse deve ser um número muito pequeno, como 2 a 50 ou menos.

Trabalhar com isso mostra que a chance de colisão entre os 500k identificadores de 15 caracteres é de cerca de 2 a 52 , o que provavelmente é menos provável do que erros não detectados dos raios cósmicos, etc.

Comparação com UUIDs

De acordo com suas especificações, os UUIDs não foram projetados para serem imprevisíveis e não devem ser usados ​​como identificadores de sessão.

Os UUIDs em seu formato padrão ocupam muito espaço: 36 caracteres para apenas 122 bits de entropia. (Nem todos os bits de um UUID "aleatório" são selecionados aleatoriamente.) Uma sequência alfanumérica escolhida aleatoriamente embala mais entropia em apenas 21 caracteres.

UUIDs não são flexíveis; eles têm uma estrutura e layout padronizados. Esta é sua principal virtude, bem como sua principal fraqueza. Ao colaborar com uma parte externa, a padronização oferecida pelos UUIDs pode ser útil. Para uso puramente interno, eles podem ser ineficientes.


6
Se você precisar de espaços no seu, você pode ir .replaceAll("\\d", " ");até o final da return new BigInteger(130, random).toString(32);linha para fazer uma troca de regex. Ele substitui todos os dígitos por espaços. Funciona muito bem para mim: eu estou usando isso como um substituto para um front-end Lorem Ipsum
weisjohn

4
@weisjohn Essa é uma boa ideia. Você pode fazer algo semelhante com o segundo método, removendo os dígitos symbolse usando um espaço; você pode controlar o tamanho médio da "palavra" alterando o número de espaços nos símbolos (mais ocorrências para palavras mais curtas). Para uma solução de texto falso realmente exagerada, você pode usar uma cadeia de Markov!
21711

4
Esses identificadores são selecionados aleatoriamente no espaço de um determinado tamanho. Eles podem ter 1 caractere. Se você quiser um comprimento fixo, poderá usar a segunda solução, com uma SecureRandominstância atribuída à randomvariável.
Erickson

15
Por que .toString (32) em vez de .toString (36)?
ejain

17
@ejain porque 32 = 2 ^ 5; cada caractere representará exatamente 5 bits e 130 bits podem ser divididos igualmente em caracteres.
21712

817

Java fornece uma maneira de fazer isso diretamente. Se você não quer os traços, eles são fáceis de remover. Apenas useuuid.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;
    }
}

Resultado:

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

33
Lembre-se de que esta solução gera apenas uma sequência aleatória com caracteres hexadecimais. O que pode ser bom em alguns casos.
Dave

5
A classe UUID é útil. No entanto, eles não são tão compactos quanto os identificadores produzidos por minhas respostas. Isso pode ser um problema, por exemplo, nos URLs. Depende das suas necessidades.
24611 erickson

6
@Ruggs - O objetivo é seqüências alfanuméricas. Como a ampliação da saída para quaisquer bytes possíveis se encaixa nisso?
21411

72
De acordo com o RFC4122, usar UUIDs como tokens é uma péssima idéia: não assuma que os UUIDs sejam difíceis de adivinhar; eles não devem ser usados ​​como recursos de segurança (identificadores cuja mera posse concede acesso), por exemplo. Uma fonte previsível de números aleatórios agravará a situação. ietf.org/rfc/rfc4122.txt
Somatik

34
UUID.randomUUID().toString().replaceAll("-", "");torna a sequência alfanumérica, conforme 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();
}

61
+1, a solução mais simples aqui para gerar uma sequência aleatória de comprimento especificado (além de usar RandomStringUtils da Commons Lang).
Jonik

12
Considere usar em SecureRandomvez da Randomclasse. Se as senhas forem geradas em um servidor, elas poderão ficar vulneráveis ​​a ataques de tempo.
foens

8
Eu também adicionaria letras minúsculas: AB = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";e alguns outros caracteres permitidos.
ACV

1
Por que não colocar static Random rnd = new Random();dentro do método?
Micro

4
@MicroR Existe um bom motivo para criar o Randomobjeto em cada invocação de método? Acho que não.
Cassiomolin

484

Se você está feliz em usar as classes do Apache, você pode usar org.apache.commons.text.RandomStringGenerator(texto comum).

Exemplo:

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

Desde o commons-lang 3.6, RandomStringUtilsestá obsoleto.


22
Acabou de examinar a classe mencionada de Apache Commons Lang 3.3.1biblioteca - e está usando apenas java.util.Randompara fornecer seqüências aleatórias, produzindo seqüências inseguras .
Yuriy Nakonechnyy 03/04

16
Certifique-se de usar o SecureRandom ao usar o 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 03/03

NÃO USE. Isso cria sequências inseguras !
Patrick Favre

110

Você pode usar a biblioteca Apache para isso: RandomStringUtils

RandomStringUtils.randomAlphanumeric(20).toUpperCase();

18
@ Kamil, eu olhei o código fonte para RandomStringUtils, e ele usa uma instância de java.util.Random instanciado sem argumentos. A documentação para java.util.Random diz que usa a hora atual do sistema se nenhuma semente for fornecida. Isso significa que ele não pode ser usado para identificadores / chaves de sessão, pois um invasor pode prever facilmente quais são os identificadores de sessão gerados a qualquer momento.
Inshallah

36
@ Inshallah: Você está (desnecessariamente) fazendo uma engenharia excessiva do sistema. Embora eu concorde que ele usa o tempo como semente, o invasor precisa ter acesso aos seguintes dados para obter o que deseja: 1. Tempo para o milissegundo exato em que o código foi propagado. 2. Número de chamadas que ocorreram até agora 3. Atomicidade para sua própria ligação (para que o número de ligações até agora seja o mesmo) Se o atacante tiver todas essas três coisas, você terá um problema muito maior em mãos ...
Ajeet Ganga

3
dependência Gradle: compile 'commons-lang:commons-lang:2.6'
younes0

4
@ Ajeet isso não é verdade. Você pode derivar o estado do gerador de números aleatórios a partir de sua saída. Se um invasor puder gerar alguns milhares de chamadas para gerar tokens de API aleatórios, o invasor poderá prever todos os tokens de API futuros.
Thomas Grainger

3
@AjeetGanga Nada a ver com a engenharia. Se você deseja criar IDs de sessão, precisa de um gerador pseudo-aleatório criptográfico. Toda impressão que utiliza o tempo como semente é previsível e muito insegura para dados que devem ser imprevisíveis. Basta usar SecureRandome você é bom.
Patrick Favre

105

Em uma linha:

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

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


9
Mas apenas 6 letras :(
Moshe Revah

2
Ele me ajudou muito, mas apenas dígitos hexadecimais :(
noquery

@Zippoxer, você poderia concat que várias vezes =)
daniel.bavrin

7
O exemplo do OP mostrou a seguinte String como um exemplo AEYGF7K0DM1Xque não é hexadecimal. Preocupa-me quantas vezes as pessoas confundem alfanumérico com hexadecimal. Eles não são a mesma coisa.
hfontanez

6
Isso é muito menos aleatório do que o comprimento da string, pois Math.random()produz doubleentre 0 e 1; portanto, a parte do expoente geralmente não é utilizada. Use random.nextLongpara um aleatório em longvez deste truque feio.
Maaartinus

80

Isso é facilmente possível sem bibliotecas externas.

1. Geração de dados pseudo-aleatórios criptográficos

Primeiro, você precisa de um PRNG criptográfico. O Java tem SecureRandomisso e normalmente usa a melhor fonte de entropia na máquina (por exemplo /dev/random). Leia mais aqui.

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

Nota: SecureRandom é a maneira mais lenta, mas mais segura, em Java, de gerar bytes aleatórios. No entanto, recomendo NÃO considerar o desempenho aqui, pois geralmente não tem impacto real no seu aplicativo, a menos que você precise gerar milhões de tokens por segundo.

2. Espaço Necessário de Valores Possíveis

Em seguida, você deve decidir "quão único" seu token precisa ser. O único e único ponto de considerar a entropia é garantir que o sistema resista a ataques de força bruta: o espaço de valores possíveis deve ser tão grande que qualquer invasor possa tentar apenas uma proporção desprezível dos valores em tempo não ridículo 1 . Identificadores exclusivos, como aleatórios, por serem exclusivos para basicamente todos os casos de uso, mas o mais extremo, e você não precisa pensar em duplicatas. Aqui está uma tabela de comparação simples de entropia, incluindo uma análise simples doUUID têm 122 bits de entropia (ou seja, 2 ^ 122 = 5,3x10 ^ 36) - a chance de colisão é "* (...) para haver uma chance em um bilhão de duplicação, versão de 103 trilhões de versões 4 UUIDs devem ser gerados 2 ". Escolheremos 128 bits, pois ele se encaixa exatamente em 16 bytes e é visto como altamente suficiente problema aniversário .

comparação de tamanhos de token

Para requisitos simples, o comprimento de 8 ou 12 bytes pode ser suficiente, mas com 16 bytes você está no "lado seguro".

E é basicamente isso. A última coisa é pensar na codificação para que ela possa ser representada como um texto imprimível (leia, a String).

3. Codificação binária para texto

Codificações típicas incluem:

  • Base64cada caractere codifica 6 bits, criando uma sobrecarga de 33%. Felizmente, existem implementações padrão em Java 8+ e Android . Com o Java antigo, você pode usar qualquer uma das inúmeras bibliotecas de terceiros . Se você deseja que seus tokens sejam seguros, use a versão segura do URL do RFC4648 (que geralmente é suportada pela maioria das implementações). Exemplo de codificação de 16 bytes com preenchimento:XfJhfv3C0P6ag7y9VQxSbw==

  • Base32cada caractere codifica 5 bits, criando uma sobrecarga de 40%. Isso o utilizará A-Ze o tornará 2-7razoavelmente eficiente em termos de espaço, além de ser alfanumérico que não diferencia maiúsculas de minúsculas. Não há implementação padrão no JDK . Exemplo de codificação de 16 bytes sem preenchimento:WUPIL5DQTZGMF4D3NX5L7LNFOY

  • Base16(hex) todos os caracteres codificam 4 bits, exigindo 2 caracteres por byte (ou seja, 16 bytes criam uma cadeia de comprimento 32). Portanto, hex é menos eficiente em termos de espaço do que Base32mas é seguro para uso na maioria dos casos (url), uma vez que apenas usa 0-9e Apara F. Exemplo 16 bytes que codifica: 4fa3dd0f57cb3bf331441ed285b27735. Veja uma discussão sobre SO sobre a conversão para hexadecimal aqui.

Codificações adicionais como Base85 e a exótica Base122 existem com melhor / pior eficiência de espaço. Você pode criar sua própria codificação (o que basicamente a maioria das respostas deste segmento faz), mas eu desaconselharia se você não tiver requisitos muito específicos. Veja mais esquemas de codificação no artigo da Wikipedia.

4. Resumo e Exemplo

  • Usar SecureRandom
  • Use pelo menos 16 bytes (2 ^ 128) de valores possíveis
  • Codifique de acordo com seus requisitos (geralmente hexou base32se você precisar que seja alfanumérico)

Não

  • ... use sua codificação doméstica: melhor de manutenção e legível para outras pessoas se elas virem qual codificação padrão você usa, em vez de estranho para loops, criando chars por vez.
  • ... use UUID: não tem garantias de aleatoriedade; você está desperdiçando 6 bits de entropia e tem representação de seqüência detalhada

Exemplo: Gerador de Token Hex

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

Exemplo: Gerador de Tokens Base64 (Url Seguro)

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

Exemplo: Ferramenta Java CLI

Se você deseja uma ferramenta CLI pronta para usar, pode usar os dados: https://github.com/patrickfav/dice

Exemplo: Problema relacionado - Proteja seus IDs atuais

Se você já possui um ID, pode usar (por exemplo, um sintético longna sua entidade), mas não deseja publicar o valor interno , pode usar esta biblioteca para criptografá-lo e ofuscar: 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
Esta resposta está completa e funciona sem adicionar nenhuma dependência. Se você deseja evitar possíveis sinais de menos na saída, pode evitar BigIntegers negativos usando um parâmetro construtor: em BigInteger(1, token)vez de BigInteger(token).
31817 francoisr #

Tanques @francoisr pela dica, eu editei o exemplo de código
Patrick Favre

import java.security.SecureRandom;e import java.math.BigInteger;são necessários para fazer o exemplo funcionar, mas funciona muito bem!
precisa

Boa resposta, mas / dev / random é um método de bloqueio, por isso é lento até o ponto de bloquear se a entropia for muito baixa. O método melhor e sem bloqueio é / dev / urandom. Isso pode ser configurado via <jre> /lib/security/java.security e defina securerandom.source = file: / dev /./ urandom
Muzammil

@Muzammil Veja tersesystems.com/blog/2015/12/17/… (também linkado na resposta) - new SecureRandom()usos/dev/urandom
Patrick Favre

42

usar Dollar deve ser simples 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));
    }
}

ele gera algo assim:

DKL1SBH9UJWC
JH7P0IT21EA5
5DTI72EO6SFU
HQUMJTEBNF7Y
1HCR6SKYWGT7

é possível usar o SecureRandom com shuffle?
Iwein

34

Aqui está em 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();
  }
}

Aqui está uma amostra de execução:

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

4
Isso produzirá seqüências inseguras, isto é, sequências que podem ser facilmente adivinhadas.
Yuriy Nakonechnyy 03/04

8
Toda essa geração int aleatória e infestada com o dobro é quebrada pelo design, lenta e ilegível. Use Random#nextIntou nextLong. Mude para SecureRandomse necessário.
Maaartinus

31

Surpreendente ninguém aqui sugeriu, mas:

import java.util.UUID

UUID.randomUUID().toString();

Fácil.

O benefício disso é que os UUIDs são agradáveis ​​e longos e garantidos que são quase impossíveis de colidir.

A Wikipedia tem uma boa explicação:

"... somente depois de gerar 1 bilhão de UUIDs a cada segundo pelos próximos 100 anos, a probabilidade de criar apenas uma duplicata seria de cerca de 50%."

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

Os primeiros 4 bits são do tipo de versão e 2 para a variante, para que você obtenha 122 bits aleatoriamente. Portanto, se você quiser, pode truncar a partir do final para reduzir o tamanho do UUID. Não é recomendado, mas você ainda tem muita aleatoriedade, o suficiente para facilitar seus 500 mil registros.


39
Alguém sugeriu, cerca de um ano antes de você.
21713 Erickson

31

Uma solução curta e fácil, mas usa apenas letras minúsculas e numéricas:

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

O tamanho tem cerca de 12 dígitos na base 36 e não pode ser melhorado ainda. Claro que você pode anexar várias instâncias.


11
Lembre-se de que há 50% de chance de um sinal de menos na frente do resultado! Então, agrupar r.nextLong () em Math.abs () pode ser usado, se você não quiser o sinal de menos: Long.toString(Math.abs(r.nextLong()), 36);
Ray Hulha

5
@ RayHulha: Se você não deseja o sinal de menos, deve cortá-lo, porque, surpreendentemente, Math.abs retorna um valor negativo para Long.MIN_VALUE.
usuário desconhecido

Interessante o Math.abs retornando negativo. Mais aqui: bmaurer.blogspot.co.nz/2006/10/…
Phil

1
O problema com absé resolvido usando um operador bit a bit para limpar o bit mais significativo. Isso funcionará para todos os valores.
Radiodef

1
@ Radiodef Isso é essencialmente o que o @userunkown disse. Suponho que você também poderia fazer << 1 >>> 1.
shmosel

15

Uma alternativa no Java 8 é:

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

3
Isso é ótimo - mas se você quiser mantê-lo estritamente alfanumérico (0-9, az, AZ) veja aqui rationaljava.com/2015/06/…
Dan

12

O uso de UUIDs é inseguro, porque partes do UUID não são aleatórias. O procedimento do @erickson é muito elegante, mas não cria sequências do mesmo comprimento. O seguinte trecho deve 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 que escolher length*5. Vamos assumir o caso simples de uma sequência aleatória de comprimento 1, portanto, um caractere aleatório. Para obter um caractere aleatório contendo todos os dígitos de 0 a 9 e os caracteres az, precisaríamos de um número aleatório entre 0 e 35 para obter um de cada caractere. BigIntegerfornece um construtor para gerar um número aleatório, distribuído uniformemente pelo intervalo 0 to (2^numBits - 1). Infelizmente 35 não é um número que pode ser recebido por 2 ^ numBits - 1. Portanto, temos duas opções: Ir com 2^5-1=31ou 2^6-1=63. Se escolhermos 2^6, obteremos muitos números "desnecessários" / "mais longos". Portanto, 2^5é a melhor opção, mesmo se perdermos 4 caracteres (wz). Para gerar agora uma sequência de um determinado comprimento, podemos simplesmente usar um2^(length*numBits)-1número. O último problema, se quisermos uma string com um determinado comprimento, random poderá gerar um número pequeno, de modo que o comprimento não seja atingido, portanto, precisamos colocar a string no comprimento necessário de zeros.


você poderia explicar melhor o 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;
}

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

Então, o que isso faz é apenas adicionar a senha à string e ... sim, funciona bem, confira ... muito simples. Eu escrevi


Eu me permiti fazer algumas pequenas modificações. Por que você adiciona + 0isso com frequência? Por que você divide a declaração de mancha e inicialização? Qual é a vantagem dos índices 1,2,3,4 em vez de 0,1,2,3? Mais importante: você pegou um valor aleatório e comparou com if-else 4 vezes um novo valor, que sempre pode ser incompatível, sem obter mais aleatoriedade. Mas fique à vontade para reverter.
usuário desconhecido

8

Eu encontrei esta solução que gera uma seqüência aleatória codificada em hexadecimal. O teste de unidade fornecido parece aguentar meu caso de uso principal. Embora seja um pouco mais complexo do que algumas das outras respostas fornecidas.

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

Não acho justo falhar com tokens duplicados, que são puramente baseados em probabilidade.
Thom Wiggers

8
  1. Altere os caracteres String de acordo com seus requisitos.

  2. String é imutável. Aqui StringBuilder.appendé mais eficiente que a concatenação de cadeias.


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

3
Isso não acrescenta nada que as dezenas de respostas fornecidas anteriormente não cobriram. E criar uma nova Randominstância em cada iteração do loop é ineficiente.
22614

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

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

7

Não gosto realmente de nenhuma das respostas relacionadas à solução "simples": S

Eu iria para um simples;), java puro, um forro (a entropia é baseada no comprimento da seqüência aleatória e no conjunto de caracteres fornecido):

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

ou (um pouco mais legível)

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

Mas, por outro lado, você também pode usar o UUID, que possui uma entropia muito boa ( https://en.wikipedia.org/wiki/Universally_unique_identifier#Collisions ):

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

Espero que ajude.


6

Você menciona "simples", mas, para o caso de alguém procurar algo que atenda a requisitos de segurança mais rigorosos, dê uma olhada no jpwgen . O jpwgen é modelado após o pwgen no Unix e é muito configurável.


Obrigado, consertou. Portanto, pelo menos existe fonte e o link é válido. Por outro lado, parece que ele não foi atualizado há algum tempo, embora eu veja que o pwgen tenha sido atualizado recentemente.
michaelok

4

Você pode usar a classe UUID com sua mensagem getLeastSignificantBits () para obter 64 bits de dados aleatórios e convertê-los em um número radix 36 (ou seja, uma string que consiste em 0-9, AZ):

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

Isso gera uma String com até 13 caracteres. Usamos Math.abs () para garantir que não haja um sinal de menos entrando.


2
Por que você usaria o UUID para obter bits aleatórios? Por que não usar apenas random.nextLong()? Ou mesmo Double.doubleToLongBits(Math.random())?
22613 Erickson

4

Você pode usar o código a seguir, se sua senha obrigatória contiver números caracteres alfabéticos especiais:

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

}

4

Aqui está o código de uma linha da AbacusUtil

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

Aleatório não significa que deve ser único. para obter strings exclusivas, usando:

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

3

Aqui está uma solução Scala:

(for (i <- 0 until rnd.nextInt(64)) yield { 
  ('0' + rnd.nextInt(64)).asInstanceOf[Char] 
}) mkString("")


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

2
Essa concatenação de strings é desnecessariamente ineficiente. E o recuo maluco torna seu código quase ilegível. É o mesmo que a idéia de Jamie, mas mal executada.
22613 Erickson

3

Eu acho que essa é a menor solução aqui, ou quase uma das menores:

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

O código funciona muito bem. Se você estiver usando esse método, recomendo que você use mais de 10 caracteres. A colisão ocorre em 5 caracteres / 30362 iterações. Isso levou 9 segundos.


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

1
Muito bom! Mas deve estar no lengthlugar do chars.lengthloop for: # for (int i = 0; i < length; i++)
Incinerator

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

3
Isso é praticamente o mesmo que a resposta de Steve McLeod dada dois anos antes.
quer
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.