임의의 영숫자 문자열을 생성하는 방법은 무엇입니까?


1741

나는 찾고 있었다 의사 난수 영숫자 문자열을 생성하는 간단한 Java 알고리즘을 있습니다. 내 상황에서 그것은 500K+세대를 초월하여 "아마도"독창적 인 고유 세션 / 키 식별자로 사용됩니다 (필요한 것은 훨씬 더 복잡한 것을 요구하지 않습니다).

이상적으로는 고유 요구 사항에 따라 길이를 지정할 수 있습니다. 예를 들어 길이가 12 인 생성 된 문자열은 다음과 같습니다 "AEYGF7K0DM1X".


150
생일 역설을 조심하십시오 .
pablosaraiva

58
생일 역설을 고려하더라도 12 개의 영숫자 문자 (총 62 개)를 사용하면 역설에 도달하기 위해서는 여전히 340 억 개가 넘는 문자열이 필요합니다. 그리고 생일 역설은 어쨌든 충돌을 보장하지는 않으며 단지 50 % 이상의 확률이라고 말합니다.
NullUserException

4
@NullUserException 50 % 성공 확률 (시도당)은 매우 높음 : 10 회 시도하더라도 성공률은 0.999입니다. 이를 염두에두고 24 시간 동안 많은 것을 시도 할 수 있다는 사실을 감안할 때 적어도 하나를 추측하기 위해 340 억 개의 문자열이 필요하지 않습니다. 이것이 일부 세션 토큰이 정말로 길어야하는 이유입니다.
Pijusn 2019 년

16
이 3 개의 단일 라인 코드는 매우 유용합니다 ..Long.toHexString(Double.doubleToLongBits(Math.random())); UUID.randomUUID().toString(); RandomStringUtils.randomAlphanumeric(12);
Manindar

18
@Pijusn 나는 이것이 오래되었다는 것을 알고 있지만 ... 생일 역설의 "50 % 확률"은 "시도 당" 이 아니며 , (이 경우) 340 억 개의 문자열 중 "50 % 확률"이 존재합니다. 최소 한 쌍의 중복 " 당신은 1.6 필요할 것 9월 시도마다 50 %의 확률이 되려면 데이터베이스의 항목 - 1.6e21 - illion합니다.
Tin Wizard

답변:


1541

연산

임의의 문자열을 생성하려면 문자열이 원하는 길이에 도달 할 때까지 허용되는 기호 세트에서 임의로 그려진 문자를 연결하십시오.

이행

다음은 임의 식별자를 생성하기위한 매우 간단하고 유연한 코드입니다. 중요한 응용 참고 사항에 대해서는 다음 정보를 읽으십시오 .

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

}

사용 예

8 자리 식별자에 대한 안전하지 않은 생성기를 만듭니다.

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

세션 식별자를위한 보안 생성기를 만듭니다.

RandomString session = new RandomString();

읽기 쉬운 코드로 생성기를 생성하여 인쇄하십시오. 더 적은 수의 기호를 사용하기 위해 문자열이 전체 영숫자 문자열보다 깁니다.

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

세션 식별자로 사용

고유 할 수있는 세션 식별자를 생성하는 것만으로는 충분하지 않거나 간단한 카운터를 사용할 수 있습니다. 공격자는 예측 가능한 식별자가 사용될 때 세션을 가로 채습니다.

길이와 보안 사이에 긴장이 있습니다. 가능성이 적기 때문에 식별자가 짧을수록 추측하기 쉽습니다. 그러나 식별자가 길수록 더 많은 스토리지와 대역폭을 소비합니다. 더 큰 기호 집합이 도움이되지만 식별자가 URL에 포함되거나 직접 입력되면 인코딩 문제가 발생할 수 있습니다.

세션 식별자에 대한 기본 난수 또는 엔트로피 소스는 암호화를 위해 설계된 난수 생성기에서 가져와야합니다. 그러나 이러한 생성기의 초기화는 때때로 계산 비용이 많이 들거나 느릴 수 있으므로 가능하면 재사용하기 위해 노력해야합니다.

객체 식별자로 사용

모든 응용 프로그램에 보안이 필요한 것은 아닙니다. 무작위 할당은 여러 엔티티가 조정 또는 파티셔닝없이 공유 공간에서 식별자를 생성하는 효율적인 방법 일 수 있습니다. 특히 클러스터 또는 분산 환경에서 조정이 느려질 수 있으며 공간을 분할하면 엔터티가 너무 작거나 큰 공유로 끝날 때 문제가 발생합니다.

대부분의 웹 응용 프로그램에서와 같이 공격자가이를보고 조작 할 수있는 경우 예측할 수없는 조치를 취하지 않고 생성 된 식별자는 다른 방법으로 보호해야합니다. 액세스 권한없이 공격자가 식별자를 추측 할 수있는 개체를 보호하는 별도의 권한 부여 시스템이 있어야합니다.

예상되는 총 식별자 수를 고려할 때 충돌을 일으키지 않을 정도로 긴 식별자를 사용하도록주의를 기울여야합니다. 이것을 "생일 역설"이라고합니다. 충돌 확률 p 는 대략 n 2 / (2q x )이며, 여기서 n 은 실제로 생성 된 식별자의 수, q 는 알파벳의 고유 한 기호의 수, x 는 식별자의 길이입니다. 2-50 이하 의 매우 작은 숫자 여야합니다 .

이 작업을 수행하면 500k 15 자 식별자 간의 충돌 가능성이 약 2‑52 임을 알 수 있습니다. 이므로 우주 광선 등에서 감지되지 않은 오류보다 가능성이 적습니다.

UUID와 비교

사양에 따라 UUID 는 예측할 수 없도록 설계 되지 않았으므로 세션 식별자로 사용 해서는 안됩니다 .

표준 형식의 UUID는 많은 공간을 차지합니다. 122 비트 엔트로피의 경우 36 자입니다. "임의의"UUID의 모든 비트가 무작위로 선택되는 것은 아닙니다. 무작위로 선택한 영숫자 문자열은 21 자만으로 더 많은 엔트로피를 압축합니다.

UUID는 융통성이 없습니다. 그것들은 표준화 된 구조와 레이아웃을 가지고 있습니다. 이것이 그들의 주요 미덕이며 주요 약점입니다. 외부 당사자와 공동 작업 할 때 UUID에서 제공하는 표준화가 도움이 될 수 있습니다. 순전히 내부 사용의 경우 비효율적 일 수 있습니다.


6
당신이 당신의 공간이 필요하다면, 당신은 정규식 스왑을 수행하기 .replaceAll("\\d", " ");위해 return new BigInteger(130, random).toString(32);줄 의 끝에 붙일 수 있습니다 . 모든 숫자를 공백으로 바꿉니다. 저에게
효과적입니다

4
@weisjohn 좋은 생각입니다. 숫자를 제거하고 symbols대신 공백을 사용하여 두 번째 방법과 비슷한 작업을 수행 할 수 있습니다 . 기호의 공백 수를 변경하여 평균 "단어"길이를 제어 할 수 있습니다 (단어가 많을수록 더 많이 나타남). 정말 위조 된 가짜 텍스트 솔루션을 위해 Markov 체인을 사용할 수 있습니다!
erickson

4
이 식별자는 특정 크기의 공간에서 임의로 선택됩니다. 길이는 1 자일 수 있습니다. 고정 길이를 원하면 변수에 SecureRandom지정된 인스턴스 와 함께 두 번째 솔루션을 사용할 수 있습니다 random.
erickson

15
왜 .toString (36)이 아닌 .toString (32)입니까?
ejain 2019

17
32 = 2 ^ 5이므로 @ejain; 각 문자는 정확히 5 비트를 나타내며 130 비트는 문자로 균등하게 나눌 수 있습니다.
erickson

817

Java는이를 직접 수행하는 방법을 제공합니다. 대시를 원하지 않으면 쉽게 제거 할 수 있습니다. 그냥 사용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;
    }
}

산출:

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

33
이 솔루션은 16 진 문자로 된 임의의 문자열 만 생성합니다. 어떤 경우에는 괜찮을 수 있습니다.
Dave

5
UUID 클래스가 유용합니다. 그러나 그들은 내 대답에 의해 생성 된 식별자만큼 작지 않습니다. 예를 들어 URL에서 문제가 될 수 있습니다. 필요에 따라 다릅니다.
erickson

6
@Ruggs-목표는 영숫자 문자열입니다. 가능한 바이트로 출력을 넓히는 것은 어떻게 맞습니까?
erickson

72
UUID를 토큰으로 사용하는 RFC4122에 따르면 나쁜 생각입니다. UUID를 추측하기 어렵다고 가정하지 마십시오. 예를 들어 보안 기능 (소유권 만 보유한 식별자)으로 사용해서는 안됩니다. 예측 가능한 난수 소스는 상황을 악화시킵니다. ietf.org/rfc/rfc4122.txt
Somatik

34
UUID.randomUUID().toString().replaceAll("-", "");요청에 따라 문자열을 영숫자로 만듭니다.
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, 지정된 길이 의 임의 문자열을 생성하는 가장 간단한 솔루션 (Commons Lang의 RandomStringUtils 사용 제외).
Jonik

12
클래스 SecureRandom대신 사용 을 고려하십시오 Random. 서버에서 암호가 생성되면 타이밍 공격에 취약 할 수 있습니다.
foens

8
나는 또한 소문자를 추가 AB = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";하고 다른 허용되는 문자를 추가 할 것 입니다.
ACV

1
static Random rnd = new Random();메소드 안에 넣지 않습니까?
마이크로

4
@MicroR Random각 메소드 호출에서 오브젝트 를 작성해야하는 이유가 있습니까? 나는 그렇게 생각하지 않습니다.
cassiomolin

484

Apache 클래스를 사용하고 싶다면 org.apache.commons.text.RandomStringGenerator(commons-text)를 사용할 수 있습니다 .

예:

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

commons-lang 3.6부터는 RandomStringUtils더 이상 사용되지 않습니다.


22
언급 된Apache Commons Lang 3.3.1 라이브러리 클래스 를 살펴 보았습니다. java.util.Random임의의 시퀀스를 제공 하는 데만 사용 하므로 안전하지 않은 시퀀스를 생성 합니다 .
Yuriy Nakonechnyy

16
RandomStringUtils를 사용할 때 SecureRandom을 사용해야합니다.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

사용하지 마세요. 이것은 안전하지 않은 시퀀스를 만듭니다 !
Patrick Favre

109

이를 위해 Apache 라이브러리를 사용할 수 있습니다. RandomStringUtils

RandomStringUtils.randomAlphanumeric(20).toUpperCase();

18
@kamil, RandomStringUtils의 소스 코드를 살펴 보았고 인수없이 인스턴스화 된 java.util.Random 인스턴스를 사용합니다. java.util.Random에 대한 문서는 시드가 제공되지 않으면 현재 시스템 시간을 사용한다고 말합니다. 이는 공격자가 생성 된 세션 식별자를 언제라도 쉽게 예측할 수 있으므로 세션 식별자 / 키에 사용할 수 없음을 의미합니다.
Inshallah

36
@Inshallah : 시스템을 과도하게 엔지니어링하고 있습니다. 시간을 시드로 사용한다는 데 동의하지만 공격자는 원하는 데이터를 실제로 얻기 위해 다음 데이터에 액세스 할 수 있어야합니다. 1. 코드가 시드 된 정확한 시간 (밀리 초)까지 2. 지금까지 발생한 호출 수 3. 자신의 전화에 대한 원 자성 (따라서 지금까지의 전화 수는 동일하다) 공격자가이 세 가지를 모두 가지고 있다면, 당신은 더 큰 문제를
안고있다

3
gradle 의존 : compile 'commons-lang:commons-lang:2.6'
younes0

4
@Ajeet 이것은 사실이 아닙니다. 난수 생성기의 상태를 출력에서 ​​파생시킬 수 있습니다. 공격자가 임의의 API 토큰을 생성하기 위해 수천 번의 호출을 생성 할 수 있으면 공격자는 향후 모든 API 토큰을 예측할 수 있습니다.
Thomas Grainger

3
@AjeetGanga 오버 엔지니어링과는 아무런 관련이 없습니다. 세션 ID를 생성하려면 암호화 의사 랜덤 생성기가 필요합니다. 시간을 시드로 사용하는 모든 프롱은 예측할 수없는 데이터에 대해 예측 가능하고 매우 안전하지 않습니다. 그냥 사용 SecureRandom하면 좋습니다.
Patrick Favre

105

한 줄로 :

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

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


9
그러나 단 6 글자 :(
Moshe Revah

2
그것은 나에게도 도움이되었지만 16 진수 만 :(
noquery

@Zippoxer, 당신은 여러 번 연결할 수 있습니다 =)
daniel.bavrin

7
OP의 예제는 AEYGF7K0DM1X16 진이 아닌 예제로 다음 문자열을 보여줍니다 . 사람들이 얼마나 자주 영숫자를 16 진수로 착각하는지 걱정됩니다. 그들은 같은 것이 아닙니다.
hfontanez

6
이 같이 문자열의 길이를 부여해야보다 훨씬 적은 랜덤 Math.random()을 생산하는 double0과 1 사이의 지수 부분이 대부분 사용되지 않도록. 이 추악한 핵 대신 random.nextLong무작위로 사용하십시오 long.
maaartinus

80

외부 라이브러리 없이도 쉽게 달성 할 수 있습니다.

1. 암호화 의사 랜덤 데이터 생성

먼저 암호화 PRNG가 필요합니다. Java는이를 SecureRandom위해 있으며 일반적으로 머신에서 최상의 엔트로피 소스를 사용합니다 (예 :) /dev/random. 자세한 내용은 여기를 참조하십시오.

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

참고 : SecureRandom 임의의 바이트를 생성하는 Java에서 가장 느리지 만 가장 안전한 방법입니다. 그러나 초당 수백만 개의 토큰을 생성하지 않는 한 일반적으로 응용 프로그램에 실제로 영향을 미치지 않으므로 성능을 고려하지 않는 것이 좋습니다.

2. 가능한 가치의 공간

다음으로, 토큰의 "유일성"을 결정해야합니다. 엔트로피를 고려하는 전체적이고 유일한 요점은 시스템이 무차별 대입 공격에 저항 할 수 있는지 확인하는 것입니다. 가능한 값의 공간이 너무 커야 만 공격자가 무해한 시간에 무시할만한 비율의 값만 시도 할 수 있습니다 1 . 랜덤과 같은 고유 식별자 UUID는 122 비트의 엔트로피 (즉, 2 ^ 122 = 5.3x10 ^ 36)를 갖습니다. 충돌 가능성은 "* (...)입니다. 10 억 번의 중복 가능성이있는 103 억 개의 버전이 있습니다. 4 개의 UUID가 2 " 로 생성되어야합니다 . 정확히 16 바이트에 맞고 충분히 높은 것으로 간주되므로 128 비트를 선택합니다.기본적으로 모든 경우에 고유하지만 가장 극단적 인 사용 사례이므로 중복에 대해 생각할 필요가 없습니다. 생일 문제 의 간단한 분석을 포함하여 엔트로피의 간단한 비교표는 다음과 같습니다 .

토큰 크기 비교

간단한 요구 사항의 경우 8 바이트 또는 12 바이트 길이이면 충분하지만 16 바이트이면 "안전한 쪽"에 있습니다.

그리고 그것은 기본적으로입니다. 마지막으로 인코딩에 대해 생각하여 인쇄 가능한 텍스트 (읽기, a String) 로 표시 할 수 있습니다 .

3. 바이너리에서 텍스트 인코딩

일반적인 인코딩에는 다음이 포함됩니다.

  • Base64모든 문자는 6 비트를 인코딩하여 33 %의 오버 헤드를 생성합니다. 다행히도 Java 8+Android 에는 표준 구현이 있습니다 . 오래된 Java를 사용하면 수많은 타사 라이브러리를 사용할 수 있습니다 . 토큰을 URL 안전 상태로 유지하려면 URL 안전 버전의 RFC4648 (일반적으로 대부분의 구현에서 지원됨)을 사용하십시오. 패딩으로 16 바이트를 인코딩하는 예 :XfJhfv3C0P6ag7y9VQxSbw==

  • Base32모든 문자는 5 비트를 인코딩하여 40 %의 오버 헤드를 생성합니다. 이것은 대소 문자를 구분하지 않고 영숫자 인 공간을 효율적으로 사용 A-Z하고 2-7효율적으로 만듭니다. JDK 에는 표준 구현 이 없습니다 . 패딩없이 16 바이트를 인코딩하는 예 :WUPIL5DQTZGMF4D3NX5L7LNFOY

  • Base16(16 진수) 모든 문자는 바이트 당 2 문자를 요구하는 4 비트를 인코딩합니다 (즉, 16 바이트는 길이가 32 인 문자열을 만듭니다). 따라서 16 진은 공간 효율적이지는 Base32않지만 0-9and 만 사용 A하기 때문에 대부분의 경우 URL을 사용하는 것이 안전 합니다 F. 16 바이트 인코딩 예 : 4fa3dd0f57cb3bf331441ed285b27735. 16 진수로 변환하는 것에 대한 SO 토론을 참조하십시오.

같은 추가적인 인코딩 Base85 와 이국적인 Base122는 더 좋은 / 나쁜 공간 효율성이 존재한다. 자신의 인코딩을 만들 수 있지만 (기본적 으로이 스레드에서 대부분의 답변이 수행함) 매우 구체적인 요구 사항이 없으면 권장하지 않습니다. 참조 위키 피 디아 문서에서 자세한 인코딩 방식을.

4. 요약 및 예

  • 사용하다 SecureRandom
  • 16 바이트 (2 ^ 128) 이상의 가능한 값을 사용하십시오.
  • 요구 사항에 따라 인코딩하십시오 (보통 hex또는 base32영숫자 인 경우)

하지마

  • ... 홈 브루 인코딩을 사용하십시오 : 한 번에 문자를 만드는 루프에 대해 이상한 for 루프 대신 어떤 표준 인코딩을 사용하는지 다른 사람들이 더 잘 유지 관리하고 읽을 수 있습니다.
  • ... UUID 사용 : 임의성을 보장하지 않습니다. 6 비트의 엔트로피를 낭비하고 자세한 문자열 표현이 있습니다.

예 : 16 진수 토큰 생성기

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

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

예 : Java CLI 도구

즉시 사용 가능한 cli 도구를 원한다면 주사위를 사용할 수 있습니다 : https://github.com/patrickfav/dice

예 : 관련 문제-현재 ID 보호

이미 사용할 수있는 ID가 있지만 (예 : long엔티티 의 합성 ) 내부 값을 게시하지 않으려 는 경우이 라이브러리를 사용하여 암호화하고 난독 처리 할 수 ​​있습니다 : https://github.com/patrickfav / id- 마스크

IdMask<Long> idMask = IdMasks.forLongIds(Config.builder(key).build());
String maskedId = idMask.mask(id);
//example: NPSBolhMyabUBdTyanrbqT8
long originalId = idMask.unmask(maskedId);

3
이 답변은 완전하며 의존성을 추가하지 않고도 작동합니다. 출력에서 빼기 기호를 피하려면 BigInteger생성자 매개 변수를 사용하여 음수를 피할 수 있습니다 . BigInteger(1, token)대신 BigInteger(token).
francoisr

힌트로 탱크 @ francoisr, 코드 예제를 편집했습니다
Patrick Favre

import java.security.SecureRandom;import java.math.BigInteger;예제 작품을 만드는 데 필요한, 그러나 그것은 잘 작동하고 있습니다!
anothermh

좋은 대답이지만 / dev / random은 엔트로피가 너무 낮 으면 차단 지점까지 느리게하는 차단 방법입니다. 더 나은 비 차단 방법은 / dev / urandom입니다. 이것은 <jre> /lib/security/java.security를 ​​통해 구성하고 securerandom.source = file : / dev /./ urandom
Muzammil을

@Muzammil 참조 tersesystems.com/blog/2015/12/17/... (또한이 질문에 링크) - new SecureRandom()사용/dev/urandom
패트릭 파브르

42

Dollar 를 사용하는 것은 다음과 같이 간단해야합니다.

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

그것은 다음과 같은 것을 출력합니다 :

DKL1SBH9UJWC
JH7P0IT21EA5
5DTI72EO6SFU
HQUMJTEBNF7Y
1HCR6SKYWGT7

셔플과 함께 SecureRandom을 사용할 수 있습니까?
iwein

34

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

다음은 샘플 실행입니다.

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

4
이것은 안전하지 않은 시퀀스, 즉 쉽게 추측 할 수있는 시퀀스를 생성 합니다.
Yuriy Nakonechnyy

8
이 double-infested random int 생성은 모두 설계 상 느리고 읽을 수 없습니다. Random#nextInt또는을 사용하십시오 nextLong. 로 전환SecureRandom필요한 경우 .
maaartinus

31

놀랍게도 여기에 아무도 제안하지 않았지만 :

import java.util.UUID

UUID.randomUUID().toString();

쉬운.

이것의 장점은 UUID가 훌륭하고 길며 충돌하기가 거의 불가능하다는 것입니다.

Wikipedia에 좋은 설명이 있습니다.

"... 향후 100 년 동안 매초 10 억 개의 UUID를 생성 한 후에 만 ​​하나의 복제본을 생성 할 확률은 약 50 %입니다."

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

처음 4 비트는 버전 유형이고 변형의 경우 2이므로 임의의 122 비트를 얻습니다. 따라서 원하는 경우 UUID 크기를 줄이기 위해 끝에서 잘릴 수 있습니다. 권장하지는 않지만 500k 레코드를 쉽게 기록 할 수있을 정도로 많은 무작위성이 있습니다.



31

짧고 쉬운 솔루션이지만 소문자와 숫자 만 사용합니다.

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

크기는 기본 36에서 12 자리 정도이며 더 이상 개선 할 수 없습니다. 물론 여러 인스턴스를 추가 할 수 있습니다.


11
결과 앞에 50 %의 마이너스 부호가있을 수 있음을 명심하십시오! 빼기 부호를 원하지 않으면 Math.abs ()에 r.nextLong ()을 래핑하여 사용할 수 있습니다. Long.toString(Math.abs(r.nextLong()), 36);
Ray Hulha

5
@RayHulha : 빼기 기호를 원하지 않으면 Math.abs가 Long.MIN_VALUE에 대해 음수 값을 반환하므로 빼야합니다.
사용자가 알 수 없음

Math.abs가 음수를 반환하는 데 관심이 있습니다. 자세한 내용은 여기 : bmaurer.blogspot.co.nz/2006/10/…
Phil

1
abs비트 연산자를 사용하여 가장 중요한 비트를 지우면 문제 가 해결됩니다. 이것은 모든 값에 적용됩니다.
Radiodef

1
@Radiodef 그것은 본질적으로 @userunkown이 말한 것입니다. 당신도 할 수 있다고 생각합니다 << 1 >>> 1.
shmosel

15

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
대단한 영숫자 (0-9, az, AZ)로 유지하려면 here rationaljava.com/2015/06/…
Dan

12

UUID의 일부가 무작위로 사용되지 않기 때문에 UUID를 사용하는 것은 안전하지 않습니다. @erickson의 절차는 매우 깔끔하지만 길이가 같은 문자열을 만들지는 않습니다. 다음 스 니펫이 충분해야합니다.

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

왜 선택 하는가 length*5. 길이가 1 인 임의의 문자열 인 임의의 문자 인 경우를 가정 해 봅시다. 모든 숫자 0-9와 문자 az를 포함하는 임의의 문자를 얻으려면 각 문자 중 하나를 얻으려면 0과 35 사이의 임의의 숫자가 필요합니다. BigInteger범위 번호에 균일하게 분포 된 난수를 생성하는 생성자를 제공합니다 . 마지막 문제는 특정 길이의 문자열을 원한다면 무작위로 작은 숫자를 생성 할 수 있으므로 길이가 충족되지 않으므로 문자열 앞에 필요한 길이를 0으로 채워야합니다.0 to (2^numBits - 1) . 불행하게도 (35)는 2 ^ 이름 비트 수신 할 수있는 수는 없다 - 우리는 두 가지 옵션이 있습니다 1. 그래서 : 함께 할 것 중 하나 2^5-1=31또는 2^6-1=63. 만약 우리가 선택한다면 우리는 2^6많은 "불필요"/ "더 긴"숫자를 얻게 될 것입니다. 따라서2^5 4자를 잃어버린 경우에도 더 나은 옵션입니다 (wz). 이제 특정 길이의 문자열을 생성하려면 간단히2^(length*numBits)-1


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

그래서 이것이하는 것은 문자열에 암호를 추가하는 것입니다 ... 그래도 잘 작동합니다 ... 매우 간단합니다. 내가 썼어


나는 약간의 수정을 허용했다. 왜 + 0자주 추가 합니까? 스팟 선언과 이니셜 라이 제이션을 왜 분리합니까? 0,1,2,3 대신 인덱스 1,2,3,4의 장점은 무엇입니까? 가장 중요한 것은 임의의 값을 취한 경우 새로운 값의 4 배인 새로운 값과 비교했을 때 더 많은 임의성을 얻지 않고 항상 불일치 할 수 있다는 것입니다. 그러나 자유롭게 롤백하십시오.
사용자가 알 수 없음

8

임의의 16 진수 인코딩 문자열을 생성하는이 솔루션을 찾았습니다. 제공된 단위 테스트는 내 주요 사용 사례를 따르는 것으로 보입니다. 그러나 다른 답변들보다 약간 더 복잡합니다.

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

순전히 확률에 근거한 중복 토큰에 실패하는 것이 공정하다고 생각하지 않습니다.
Thom Wiggers 2016 년

8
  1. 요구 사항에 따라 문자열 문자를 변경하십시오.

  2. 문자열은 변경할 수 없습니다. 다음은 StringBuilder.append문자열 연결보다 더 효율적이다.


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
이것은 이전에 주어진 수십 가지 답변을 다루지 않습니다. Random루프를 반복 할 때마다 새 인스턴스를 만드는 것은 비효율적입니다.
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;
    }
}

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

"간단한"솔루션에 관한이 답변을 정말 좋아하지 않습니다.

간단한;), 순수한 자바, 하나의 라이너 (엔트로피는 임의의 문자열 길이와 주어진 문자 세트를 기반으로 함)를 사용합니다.

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

또는 (약간 더 읽기 쉬운 구식)

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

그러나 다른 한편으로는 꽤 좋은 엔트로피가있는 UUID를 사용할 수도 있습니다 ( https://en.wikipedia.org/wiki/Universally_unique_identifier#Collisions ).

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

희망이 도움이됩니다.


6

"간단한"은 언급하지만 다른 사람이보다 엄격한 보안 요구 사항을 충족하는 것을 찾고있는 경우 jpwgen살펴볼 수 있습니다. jpwgen은 유닉스에서 pwgen 을 모델로 하며 매우 설정이 가능합니다.


고마워요 따라서 최소한 소스가 있고 링크가 유효합니다. 단점은 pwgen이 상당히 최근에 업데이트 된 것을 보았지만 한동안 업데이트 된 것처럼 보이지는 않습니다.
michaelok 2016 년

4

getLeastSignificantBits () 메시지와 함께 UUID 클래스를 사용하여 64 비트의 랜덤 데이터를 가져온 다음 기수 36 숫자 (예 : 0-9, AZ로 구성된 문자열)로 변환 할 수 있습니다.

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

최대 13 자의 문자열을 생성합니다. 우리는 Math.abs ()를 사용하여 빼기 기호가 없는지 확인합니다.


2
세계에서 왜 UUID를 사용하여 임의의 비트를 얻습니까? 왜 사용하지 random.nextLong()않습니까? 아니면 Double.doubleToLongBits(Math.random())?
erickson

4

비밀번호에 필수 영숫자 특수 문자가 포함 된 경우 다음 코드를 사용할 수 있습니다.

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

다음은 AbacusUtil 의 한 줄 코드입니다 .

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

무작위는 그것이 고유해야 함을 의미하지는 않습니다. 다음을 사용하여 고유 한 문자열을 가져옵니다.

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

3

다음은 스칼라 솔루션입니다.

(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
그 문자열 연결은 불필요하게 비효율적입니다. 그리고 들여 쓰기는 코드를 거의 읽을 수 없게 만듭니다. 이것은 Jamie의 아이디어 와 동일 하지만 제대로 실행되지 않았습니다.
erickson

3

나는 이것이 가장 작은 해결책, 또는 가장 작은 해결책이라고 생각합니다.

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

코드는 잘 작동합니다. 이 방법을 사용하는 경우 10자를 초과하는 것이 좋습니다. 충돌은 5 자 / 30362 반복에서 발생합니다. 9 초가 걸렸습니다.


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
정말 좋습니다! 그러나 for 루프 length대신에 있어야합니다 chars.length. for (int i = 0; i < length; i++)
소각로

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
이것은 2 년 전에 주어진 Steve McLeod의 답변 과 거의 같습니다 .
erickson
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.