Как сгенерировать случайную буквенно-цифровую строку

Я искал алгоритм Java просто для генерации псевдослучайной буквенно-цифровой строки. В моей ситуации он будет использоваться в качестве уникального идентификатора сеанса / ключа, который «вероятно» будет уникальным для поколения 500K+ (мои потребности на самом деле не требуют ничего более сложного).

В идеале я мог бы указать длину в зависимости от моих потребностей в уникальности. Например, сгенерированная строка длиной 12 может выглядеть примерно как "AEYGF7K0DM1X".

Остерегайтесь парадокс дня рождения.

pablosaraiva 25.10.2010 19:07

Даже принимая во внимание парадокс дня рождения, если вы используете 12 буквенно-цифровых символов (всего 62), вам все равно потребуется более 34 миллиардов строк, чтобы достичь парадокса. И парадокс дня рождения в любом случае не гарантирует столкновения, он просто говорит, что вероятность более 50%.

NullUserException 29.10.2012 08:13

@NullUserException Вероятность успеха 50% (на попытку) чертовски высока: даже при 10 попытках вероятность успеха составляет 0,999. С учетом этого и того факта, что вы можете попробовать ОЧЕНЬ МНОГО в течение 24 часов, вам не нужно 34 миллиарда строк, чтобы наверняка угадать хотя бы одну из них. Вот почему некоторые токены сеанса должны быть действительно очень длинными.

Pijusn 31.01.2015 13:28

Эта запись в блоге должна быть полезной - код для создания буквенно-цифровых строк: рациональный java.com/2015/06/…

Dan 23.06.2015 17:06

Я думаю, эти 3 однострочных кода очень полезны .. Long.toHexString(Double.doubleToLongBits(Math.random()));UUID.randomUUID().toString();RandomStringUtils.randomAlphanumeric(12);

Manindar 08.06.2016 10:31

Длина не имеет значения, поскольку она зависит от кодировки - вас интересует энтропия. Энтропия в 128 бит подходит для большинства случаев использования (например, 32 шестнадцатеричных цифры)

Patrick Favre 28.05.2017 00:31

@Pijusn Я знаю, что это устарело, но ... "шанс 50%" в парадоксе дня рождения равен НЕТ "за попытку", это "вероятность 50%, что из (в данном случае) 34 миллиардов строк существует хотя бы одна пара дубликатов ". Вам понадобится 1,6 сентябрьillion - 1,6e21 - записей в вашей базе данных, чтобы вероятность каждой попытки составляла 50%.

Tin Wizard 11.10.2017 22:21

@Walt Вы правы, я тогда немного не понял. Но даже сейчас я считаю 50% с 36 миллиардами слишком высоким риском для сеанса. Также не думайте, что использование более длинного / безопасного токена приведет к серьезным проблемам.

Pijusn 12.10.2017 12:17
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
1 811
8
1 455 133
42
Перейти к ответу Данный вопрос помечен как решенный

Ответы 42

Ответ принят как подходящий

Алгоритм

Чтобы сгенерировать случайную строку, объедините символы, выбранные случайным образом из набора допустимых символов, пока строка не достигнет желаемой длины.

Выполнение

Вот довольно простой и очень гибкий код для генерации случайных идентификаторов. Прочтите следующую информацию за важные замечания по применению.

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-адреса или повторно введены вручную.

Базовый источник случайности или энтропии для идентификаторов сеанса должен исходить от генератора случайных чисел, предназначенного для криптографии. Однако инициализация этих генераторов иногда может быть дорогостоящей или медленной с точки зрения вычислений, поэтому следует прилагать усилия для их повторного использования, когда это возможно.

Использовать как идентификаторы объекта

Не каждое приложение требует безопасности. Случайное назначение может быть эффективным способом для нескольких объектов генерировать идентификаторы в общем пространстве без какой-либо координации или разделения. Координация может быть медленной, особенно в кластерной или распределенной среде, а разделение пространства вызывает проблемы, когда объекты в конечном итоге имеют слишком маленькие или слишком большие общие ресурсы.

Идентификаторы, созданные без принятия мер по их непредсказуемости, должны быть защищены другими средствами, если злоумышленник может просматривать их и манипулировать ими, как это происходит в большинстве веб-приложений. Должна быть отдельная система авторизации, защищающая объекты, идентификатор которых злоумышленник может угадать без разрешения на доступ.

Также необходимо проявлять осторожность, чтобы использовать идентификаторы, которые имеют достаточную длину, чтобы сделать коллизии маловероятными, учитывая ожидаемое общее количество идентификаторов. Это называется «парадоксом дня рождения». Вероятность столкновения,п, приблизительно равно n2 / (2qx), где п - количество фактически сгенерированных идентификаторов, q - количество различных символов в алфавите, а Икс - длина идентификаторов. Это должно быть очень маленькое число, например 2‑50 или меньше.

Расчет этого показывает, что вероятность столкновения между 500 000 15-символьными идентификаторами составляет около 2‑52, что, вероятно, менее вероятно, чем необнаруженные ошибки космических лучей и т. д.

Сравнение с UUID

Согласно их спецификации, UUID не предназначены для непредсказуемости, и не следует может использоваться как идентификаторы сеанса.

UUID в их стандартном формате занимают много места: 36 символов всего на 122 бита энтропии. (Не все биты «случайного» UUID выбираются случайным образом.) Произвольно выбранная буквенно-цифровая строка содержит больше энтропии всего за 21 символ.

UUID не гибкие; они имеют стандартизированную структуру и расположение. Это их главное достоинство, а также их главная слабость. При сотрудничестве с внешней стороной может оказаться полезной стандартизация, предлагаемая UUID. Для чисто внутреннего использования они могут оказаться неэффективными.

твой дорогой способ у меня не работает! я не могу найти символ для метода BigInteger (int, java.security.SecureRandom)

ufk 03.02.2010 23:22

Если вам нужны пробелы в вашем, вы можете прикрепить .replaceAll("\d", " "); к концу строки return new BigInteger(130, random).toString(32);, чтобы выполнить замену регулярного выражения. Он заменяет все цифры пробелами. Отлично работает для меня: я использую это вместо интерфейса Lorem Ipsum.

weisjohn 07.10.2011 19:00

@weisjohn Хорошая идея. Вы можете сделать нечто подобное со вторым методом, удалив цифры из symbols и используя вместо них пробел; вы можете контролировать среднюю длину «слова», изменяя количество пробелов в символах (большее количество вхождений для более коротких слов). Для действительно невероятного решения с поддельным текстом вы можете использовать цепь Маркова!

erickson 07.10.2011 20:02

Как проще всего заставить метод SecureRandom создавать строки длиной 32?

Daniel Szalay 20.12.2011 01:55

@DanielSzalay Просто измените 130 на 160.

erickson 20.12.2011 02:26

Эти идентификаторы выбираются случайным образом из пространства определенного размера. Они могут состоять из 1 символа. Если вам нужна фиксированная длина, вы можете использовать второе решение с экземпляром SecureRandom, назначенным переменной random.

erickson 20.12.2011 04:15

Почему .toString (32), а не .toString (36)?

ejain 21.02.2012 23:13

@ejain, потому что 32 = 2 ^ 5; каждый символ будет представлять ровно 5 бит, а 130 бит могут быть равномерно разделены на символы.

erickson 22.02.2012 01:38

@erickson BigInteger.toString(int) не работает таким образом, он фактически вызывает Long.toString(long, String) для определения значений символов (что дает лучшее описание JavaDoc того, что он на самом деле делает). По сути, выполнение BigInteger.toString(32) означает, что вы получаете только символы 0-9 + a-v, а не 0-9 + a-z.

Thor84no 29.08.2012 19:51

@ Thor84no А как вы думаете, что я говорил о том, как это работает?

erickson 29.08.2012 20:25

@erickson Я не знаю, что вы говорили об этом, но, похоже, в него входили биты, которые метод BigInteger.toString(int) никогда не использует. Он использует char[], и я не понимаю, насколько 130 бит уместно в каком-либо отношении. Вы также, кажется, говорите, что использование 32 вместо 36 дает некоторую пользу, и я не вижу никаких доказательств этого. Это не значит, что я не мог чего-то упустить, но ваше объяснение не делает этого очевидным.

Thor84no 30.08.2012 04:05

@ Thor84no Утверждение, что метод "не работает", подразумевает, что у вас есть четкое представление о том, что я говорю, и что то, что я говорил, было неправильным. Как бы то ни было, для надежной защиты предпочтительно использовать не менее 128 бит. 25 цифр с основанием 32 будут содержать только 125 бит, поэтому вам нужно 26 цифр с основанием 32. Но 32 ^ 26 в точности равно 2 ^ 130, поэтому вы можете втиснуть пару дополнительных бит без каких-либо дополнительных символов. Если вместо этого вы используете основание 36, вы можете уместить 129 бит в 25 символов, но при этом будет потрачено немного места (четверть бита).

erickson 30.08.2012 08:16

Что предпочтительнее - первое решение или UUID.randomUUID ();

sandy 18.04.2013 10:50

public String nextString (int lenOfStr) Были бы какие-либо недостатки в создании функции, которая принимает длину как параметр и перемещает char [] buf внутри этой функции?

tgkprog 30.04.2013 20:01

@tgkprog Ты определенно сможешь это сделать. Однако вам не нужно делать buf локальной переменной; просто измените границы цикла и используйте new String(buf, 0, lenOfStr).

erickson 30.04.2013 22:23

Я читал давно, что локальные вары быстрее. поэтому я подумал: char [] buf = new char [lenOfStr]; как первая строка функции. это будет безопаснее для нескольких потоков, обращающихся к нему.

tgkprog 30.04.2013 22:37

Локальная переменная может быть быстрее при определенных условиях, но выделение нового массива при каждом вызове очень вероятно приведет к стиранию любого прироста скорости. Этот код не является потокобезопасным, но если вы сделаете его потокобезопасным, поместив буфер в стек, у вас может возникнуть конкуренция за экземпляр Random. Я не знаю, зачем вам нужно делиться экземплярами между потоками.

erickson 01.05.2013 00:01

Отличный ответ, но генерируются только числовые значения? Первоначальный вопрос касался генерации случайных буквенно-цифровых значений.

Robert Kang 02.01.2014 22:08

@RobertKang Нет, оба метода дают буквенно-цифровые результаты. Во-первых, потому что представление чисел в формате base-32 в Java включает буквы, а во-вторых, потому что набор символов включает буквы.

erickson 02.01.2014 22:44

@Synderesis Я добавил абзац. Это то, о чем вы спрашивали?

erickson 01.05.2014 21:38

@erickson Да, большое спасибо! Поскольку мы используем базу 32, значит ли это, что мы не получим все возможные буквы от a до z? Я полагаю, это не имеет значения с точки зрения безопасности, но я просто хочу убедиться, что я правильно понимаю.

Richard Fung 02.05.2014 02:27

@Synderesis Верно, вы не увидите все буквы в результатах. Компактность - это компромисс: если вас устраивают специальные символы, можно использовать кодировку base-64 или даже кодировку base-85, в которой используется много символов. Но если вы используете их в URL-адресах, это может быть сложно кодировать. Общий принцип состоит в том, чтобы округлить количество битов, чтобы вы использовали полную емкость каждой «цифры» в кодировании.

erickson 02.05.2014 02:57

@erickson Я использую это, чтобы сгенерировать пароль длиной 11 с строчными буквами, заглавными буквами и цифрами. Гарантирует ли это, что пароль будет содержать хотя бы один из них? если нет, то каковы шансы, что это не так? Я представляю себе довольно стройную.

PT_C 12.09.2014 22:49

@PT_C Нет, это не гарантирует этого. Вы хотите заполнить подмассивы соответствующей длины символами каждого типа, а затем оставшуюся часть символами всех типов. Затем перемешайте весь массив. Например, первый элемент будет случайным образом выбран из верхнего регистра, следующий из нижнего регистра, следующий из цифры и последние 8 из всего набора. Затем перетасуйте позиции выбранных персонажей с помощью Фишера-Йейтса. Я не показываю это здесь, потому что он больше ориентирован на идентификаторы, чем на пароли. Мне нравятся проходные фразы.

erickson 12.09.2014 23:24

Как строка, сгенерированная этим кодом, более компактна / эффективна, чем строка такой же длины, но со всеми буквами английского алфавита? Вы понимаете, как текст кодируется в байты и сколько места требуется для его хранения / передачи? Поэтому, если вы не ожидаете, что люди будут хранить / передавать сгенерированную строку, используя наиболее компактное представление для них в байтах, строка, сгенерированная вашим кодом, фактически тратит пространство.

Rodrigo Quesada 11.05.2015 18:52

@RodrigoQuesada UUID, к которым относится мой ответ, содержат несколько символов, которые не являются случайными, что снижает их эффективность. Что касается вашего комментария о «бесполезной трате места», относительно чего? Этот вопрос касается именно буквенно-цифровые строки,, а не их закодированной формы.

erickson 11.05.2015 19:01

Что ж, если вы говорите о битах, вы подразумеваете некоторую форму кодирования (надеюсь, в байтах?). Иначе о них нет смысла говорить. В любом случае, я думаю, вам следует подробнее рассказать о своем ответе о том, при каких обстоятельствах то, что вы утверждаете, остается верным при его реализации с использованием языка программирования (о да, это сайт для программистов, кстати, и мы обычно предпочитаем кодировать информацию в байтов). Кроме того, я думаю, вы забыли добавить, что этот вопрос также касается Java.

Rodrigo Quesada 11.05.2015 19:19

@RodrigoQuesada Нет, я говорю о битах энтропии. Сколько информации содержит данная строка? Итак, проверьте свои предположения, а затем посмотрите, можете ли вы предоставить конкретный пример, где другая строка может упаковать больше энтропии в более короткую строку того же алфавита.

erickson 11.05.2015 19:22

Прекрасно, мне интересно, сколько людей могут догадаться, что вы полностью игнорируете реальное использование памяти / хранилища в этом ответе, вы должны четко понимать это, давая чисто теоретические ответы в потоке переполнения стека (могу ли я снова предложить отредактировать ваш ответ?) в противном случае люди могут ошибочно подумать, что предоставленный вами код (что, надеюсь, вы всегда делаете для такого рода вопросов?) - лучший вариант.

Rodrigo Quesada 11.05.2015 19:45

@RodrigoQuesada О каком решении вы говорите? Первый отмечен как «более дорогой» из-за повышенных требований к вычислениям и памяти. Второй считается более эффективным, но менее безопасным. Это быстрее и максимально эффективно при любой реальной кодировке. Опять же, я все еще ищу от вас контрпример.

erickson 11.05.2015 19:53

Круто, вы, вероятно, захотите добавить то, что вы только что сказали, к своему ответу, это может помочь его улучшить. Что касается контрпримера, если вы говорите о предоставлении «примера, в котором другая строка может упаковать больше энтропии в более короткую строку того же алфавита», ошибаюсь ли я, предполагая, что этого ответа не существует, и поэтому глупо его ждать? В любом случае, что меня интересует (ну, может быть, не очень), так это то, что вы поясняете этот ответ (хотя и не в комментариях), чтобы другие люди могли лучше судить при анализе вариантов.

Rodrigo Quesada 11.05.2015 20:21

@DanielSzalay, другие ответы не оправдали ваших ожиданий, даже с 160, результатом могут быть строки длиной 31. Я создал небольшой класс держателей. / * * Генератор случайных чисел, используемый этим классом для создания случайных ключей. * В классе-держателе, чтобы отложить инициализацию до необходимости. * / частный статический класс RandomHolder {static final Random random = new SecureRandom (); общедоступный статический String randomKey (int length) {String key; while ((ключ = новый BigInteger (длина * 5 / * база 32,2 ^ 5 * /, случайный) .toString (32)). length () <длина); ключ возврата; }}

Kristian Kraljic 04.07.2015 01:03

@erickson Есть ли конкретная причина для выбора 130 бит в базе 32? Почему бы не использовать 128 бит в базе 16 (шестнадцатеричный)? Разве это не было бы похоже с точки зрения безопасности?

djule5 07.10.2015 02:27

@ djule5 130 бит в Base32 дает 4-кратную безопасность в 81% пространства по сравнению со 128 битами в шестнадцатеричном формате. Но вы правы, 130 было округлено от 128, потому что 128 бит считается надежной защитой.

erickson 07.10.2015 07:39

@erickson Не могли бы вы объяснить второй подход. Я несколько раз тестировал класс RandomString с генерацией 10 миллионов строк. И каждый раз я получаю уникальный набор строк, но насколько я правильно понял, ваш класс не гарантирует уникальных наборов?

Iurii 06.11.2015 13:55

@Iurii Ни один из подходов не гарантирует уникальных наборов; если бы они это сделали, они не были бы случайными. Однако во втором примере используется «линейный конгруэнтный генератор», и, изучая последовательные результаты, можно предсказать все будущие результаты. Другая проблема заключается в том, что в конечном итоге вывод повторяется. Криптографический генератор случайных чисел разработан, чтобы избежать этих проблем, так что даже если злоумышленник сможет наблюдать все сгенерированные значения, он не сможет предсказать какие-либо будущие значения. Необходимость этого уровня безопасности зависит от вашего приложения.

erickson 06.11.2015 19:39

Я не понимаю, почему количество символов, получаемых в результате, всегда меняется. Основываясь на том, что мы генерируем 130 бит в базе 32, не должны ли результирующие строки всегда иметь одинаковую длину? Я получаю строки из 24–26 символов.

Christian Vielma 24.11.2015 13:57

@ChristianVielma Если достаточное количество битов самого высокого порядка равны нулю, идентификатор может быть короче. Чтобы все они были одинаковой длины, вы можете дополнить начало строки нулями.

erickson 24.11.2015 19:38

Спасибо! @erickson, так возможно ли, чтобы вся строка была пустой, если все биты равны 0?

Christian Vielma 26.11.2015 12:20

@ChristianVielma Строка может быть "0", но не пуста.

erickson 28.11.2015 05:48

Я сгенерировал несколько идентификаторов сеансов, используя это, однако ни один из них не имеет заглавных букв (все маленькие буквы). Есть ли способ включить заглавные буквы? Кстати, я использую первый код с использованием SecureRandom.

M-D 18.04.2016 18:55

для быстрого: stackoverflow.com/questions/26845307/…

iAhmed 26.05.2016 14:25

@ M-D NO, вы не можете получить прописные буквы из toString (), так как максимально возможное основание системы счисления составляет 36, т.е. с использованием чисел и строчных букв. - из java.lang.Character Javadoc: public static final int MAX_RADIX = 36 Максимальное основание системы счисления, доступное для преобразования в строки и из строк. Постоянное значение этого поля - это наибольшее значение, разрешенное для аргумента radix в методах преобразования системы счисления, таких как метод digit, метод forDigit и метод toString класса Integer.

RobMcZag 10.12.2016 14:11

@erickson, вы правы, что алфавит из 32 и 130 бит с результирующей длиной строки 26 идеально подходит и математически элегантен, но если людям просто нужны короткие URL-адреса для своих токенов, возможно, используя 129 бит, а алфавит из 36 символов делает больше толку, если потом получить макс. длина строки 25? Не то чтобы это вообще имеет значение, но я думаю, что люди хотят получить максимальную отдачу (бит) за доллар (длина строки). Имеет смысл?

Michael Böckling 05.01.2017 18:21

@ MichaelBöckling Да, первый метод написан именно так, как следствие того, как работает BigInteger.toString(), и он не обеспечивает согласованной длины вывода или максимального количества бит энтропии на символ. Если вам нужна максимальная безопасность и максимальная эффективность, я бы использовал второй метод, но инициализировал random экземпляром SecureRandom.

erickson 05.01.2017 20:32

В однострочном виде: new BigInteger(130, new SecureRandom()).toString(36) (или, я думаю, измените 36 на 62, чтобы включить заглавные буквы)

Hack5 07.07.2017 10:41

@Penn Изначально я так не писал, потому что заполнение безопасного ГСЧ в различных версиях Java было блокирующей операцией, которая может привести к блокировке на несколько минут по мере истощения системной энтропии. В текущих версиях это должно быть нормально, если кто-то явно (и ошибочно) не выбрал блокирующий ГСЧ.

erickson 08.07.2017 06:05

Одно небольшое замечание по поводу кода - превращение buf в поле, а не в локальную переменную делает его нереентерабельным. Лучше, IMHO, сделать buf локальной переменной в методе nextString().

DaBlick 21.09.2017 21:36

@DaBlick Конечно. Это иллюстрация, которая может быть адаптирована к конкретным требованиям. Если вы хотите использовать его как есть, знайте, что он написан для эффективного использования одним потоком.

erickson 21.09.2017 21:41

Я хочу сохранить это уникальное значение внутри строки. Я вызываю RandomString session = new RandomString();, а затем session.toString () не дает мне строки! Как мне получить доступ к строке?

Aekansh Dixit 31.01.2018 15:48

@AekanshDixit String sessionId = session.nextString(); Сохраните экземпляр генератора и продолжайте использовать его для генерации новых идентификаторов всякий раз, когда вам нужно.

erickson 31.01.2018 17:22

этот код имеет ошибку каждый раз, когда он создает одно и то же случайное число

Muhammad Waqas 14.01.2020 13:50

@MuhammadWaqas Нет, это не так. Если у вас все еще возникают проблемы, вы должны опубликовать новый вопрос с кодом ваш, чтобы мы могли определить проблему. Вы можете прокомментировать здесь ссылку на свой вопрос.

erickson 14.01.2020 16:22

когда мы используем этот "ThreadLocalRandom.current ()", он возвращает в основном случайное число

Muhammad Waqas 15.01.2020 17:05

@MuhammadWaqas Значит, теперь он работает? Или вы по-прежнему генерируете больше дубликатов, чем ожидалось? В последнем случае откройте новый вопрос с минимальный воспроизводимый пример и описанием результатов, чтобы было понятно, что вы видите.

erickson 15.01.2020 18:12

Версия Kotlin (и сценарий Kotlin для запуска прямо из консоли): gist.github.com/corlaez/c73351af76cc6b36c342170534b29bf6

corlaez 10.06.2020 20:37

Вот это на 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

Это создаст небезопасные последовательности, то есть последовательности, которые можно легко угадать.

Yuriy Nakonechnyy 03.04.2014 18:53

Вся эта генерация случайных int с двойным заражением нарушена по дизайну, медленна и нечитаема. Используйте Random#nextInt или nextLong. При необходимости переключитесь на SecureRandom.

maaartinus 22.07.2015 04:17

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

Помните, что это решение генерирует только случайную строку с шестнадцатеричными символами. В некоторых случаях это может быть нормально.

Dave 05.05.2011 13:28

Класс UUID полезен. Однако они не такие компактные, как идентификаторы, полученные в моих ответах. Это может быть проблемой, например, в URL-адресах. Зависит от ваших потребностей.

erickson 24.08.2011 20:37

Если вас беспокоят шестнадцатеричные символы, просто используйте криптографический алгоритм хеширования.

Ruggs 06.09.2011 04:13

@Ruggs - Цель - буквенно-цифровые строки. Как с этим согласуется расширение вывода до любых возможных байтов?

erickson 07.10.2011 20:18

Согласно RFC4122 использование UUID в качестве токенов - плохая идея: не думайте, что UUID трудно угадать; они не должны использоваться, например, в качестве средств защиты (идентификаторы, простое владение которыми предоставляет доступ). Предсказуемый источник случайных чисел усугубит ситуацию. ietf.org/rfc/rfc4122.txt

Somatik 31.12.2012 15:31

@Somatik - Так что же использовать вместо UUID?

Drew S 14.11.2013 21:04

@TheDrizzle Я полагаю, это один из других высоко оцененных ответов

Somatik 15.11.2013 12:22

UUID.randomUUID().toString().replaceAll("-", ""); делает строку буквенно-цифровой, как требуется.

Numid 22.01.2014 13:58

@Numid Я никогда не видел что-то между g и z в UUID.

Patrick Bergner 11.02.2014 18:33

@PatrickBergner прав. Приведенное выше предложение дает только последовательность шестнадцатеричных цифр.

Numid 12.02.2014 11:09

Что насчет MD5 на этом выходе? Это должно быть труднее угадать.

uriel 02.05.2015 22:46

Просто используйте базу 64, если хотите, чтобы она была хешированной и буквенно-числовой.

ThePyroEagle 24.12.2015 14:24

@Somatik UUID.randomUUID () фактически использует SecureRandom. Тем не менее, возможно, это не лучшая идея, если вам нужно 128-битное шифрование. Вы получите только 122 бита случайного: stackoverflow.com/questions/7532807/…

Micro 08.02.2016 04:10

Это сгенерирует строку из 36 символов. (32 шестнадцатеричных цифры + 4 тире), не более.

Charles Follet 03.01.2017 16:44

Я нашел это решение, которое генерирует случайную строку в шестнадцатеричном коде. Представленный модульный тест, похоже, соответствует моему основному варианту использования. Хотя это немного сложнее, чем некоторые другие предоставленные ответы.

/**
 * 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 02.06.2012 19:22

Если вы счастливы использовать классы Apache, вы можете использовать org.apache.commons.text.RandomStringGenerator (Текст Apache Commons).

Пример:

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

Начиная с Apache Commons Lang 3.6, RandomStringUtils устарел.

Только что просмотрел упомянутый класс библиотеки Apache Commons Lang 3.3.1 - и он использует только java.util.Random для предоставления случайных последовательностей, поэтому он создает небезопасные последовательности.

Yuriy Nakonechnyy 03.04.2014 18:51

Убедитесь, что вы используете SecureRandom при использовании 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.2015 16:28

НЕ ИСПОЛЬЗОВАТЬ. Это создает небезопасные последовательности!

Patrick Favre 04.04.2019 16:03

Создайте свой RandomStringGenerator, используя это, чтобы последовательности были безопасными: new RandomStringGenerator.Builder().usingRandom(RANDOM::nextInt)‌​.build();

Rohan 15.09.2020 00:36

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

+1, простейшее решение для генерации случайной строки указанной длины (кроме использования RandomStringUtils из Commons Lang).

Jonik 20.04.2012 19:49

Рассмотрите возможность использования SecureRandom вместо класса Random. Если пароли генерируются на сервере, он может быть уязвим для временных атак.

foens 25.06.2014 17:34

Я бы также добавил строчные буквы: AB = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvw‌​xyz"; и некоторые другие разрешенные символы.

ACV 07.09.2015 23:56

Спасибо! Я также добавил строчные буквы и некоторые специальные символы.

Robert Martin 07.01.2016 00:46

Почему бы не поставить static Random rnd = new Random(); внутрь метода?

Micro 08.02.2016 04:25

@MicroR Есть ли веская причина для создания объекта Random при каждом вызове метода? Я так не думаю.

cassiomolin 15.02.2016 13:49

для быстрого: stackoverflow.com/questions/26845307/…

iAhmed 26.05.2016 14:25

Я разработал приложение для разработки автоматически сгенерированной буквенно-цифровой строки для моего проекта. В этой строке первые три символа являются алфавитными, а следующие семь - целыми числами.

public class AlphaNumericGenerator {

    public static void main(String[] args) {
        java.util.Random r = new java.util.Random();
        int i = 1, n = 0;
        char c;
        String str = "";
        for (int t = 0; t < 3; t++) {
            while (true) {
                i = r.nextInt(10);
                if (i > 5 && i < 10) {

                    if (i == 9) {
                        i = 90;
                        n = 90;
                        break;
                    }
                    if (i != 90) {
                        n = i * 10 + r.nextInt(10);
                        while (n < 65) {
                            n = i * 10 + r.nextInt(10);
                        }
                    }
                    break;
                }
            }
            c = (char)n;

            str = String.valueOf(c) + str;
        }

        while(true){
            i = r.nextInt(10000000);
            if (i > 999999)
                break;
        }
        str = str + i;
        System.out.println(str);
    }
}

В одной строке:

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

Источник: Java - создание случайной строки

Мне тоже помогло, но только шестнадцатеричные цифры :(

noquery 05.09.2011 09:31

@Zippoxer, это можно конкатенировать несколько раз =)

daniel.bavrin 17.05.2014 19:10

Пример OP показал следующую строку в качестве примера AEYGF7K0DM1X, который не является шестнадцатеричным. Меня беспокоит, как часто люди путают буквенно-цифровые числа с шестнадцатеричными. Это не одно и то же.

hfontanez 20.11.2014 05:31

@ daniel.bavrin, Zippoxer означает, что шестнадцатеричная строка состоит только из 6 букв (ABCDEF). Он не говорит о длине, неважно, сколько раз вы конкатенируете

jcesarmobile 16.01.2015 11:34

Это гораздо менее случайное значение, чем должно быть задано длиной строки, поскольку Math.random() производит double между 0 и 1, поэтому экспоненциальная часть в основном не используется. Используйте random.nextLong для случайного long вместо этого уродливого взлома.

maaartinus 22.07.2015 04:13

Использование Доллар должно быть таким же простым, как:

// "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 16.11.2016 13:58

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

Вы упомянули «простой», но на всякий случай, если кто-то еще ищет что-то, отвечающее более строгим требованиям безопасности, вы можете взглянуть на jpwgen. jpwgen смоделирован на основе pwgen в Unix и очень настраиваемый.

Спасибо, исправил. Так что, по крайней мере, есть источник и ссылка действительна. С другой стороны, не похоже, что он обновлялся какое-то время, хотя я вижу, что pwgen был обновлен довольно недавно.

michaelok 27.06.2017 01:50

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

import java.util.Random;

public class passGen{
    // Version 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 StringBuilder pass = new StringBuilder();

    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(26);
                pass.append(dCase.charAt(spot));
            } else if (rPick == 1) {
                int spot = r.nextInt(26);
                pass.append(uCase.charAt(spot));
            } else if (rPick == 2) {
                int spot = r.nextInt(8);
                pass.append(sChar.charAt(spot));
            } else {
                int spot = r.nextInt(10);
                pass.append(intChar.charAt(spot));
            }
        }
        System.out.println ("Generated Pass: " + pass.toString());
    }
}

Это просто добавляет пароль в строку и ... да, работает хорошо. Проверьте это ... Это очень просто; Я это написал.

Я позволил себе внести небольшие изменения. Почему вы так часто добавляете + 0? Почему вы разделяете объявление пятна и инициализацию? В чем преимущество индексов 1,2,3,4 вместо 0,1,2,3? Самое главное: вы взяли случайное значение и 4 раза сравнили с if-else новое значение, которое всегда может не совпадать, без увеличения случайности. Но смело откатывайтесь.

user unknown 17.04.2012 13:50

Краткое и простое решение, но в нем используются только строчные буквы и числа:

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

Размер составляет примерно 12 цифр до 36 и не может быть улучшен таким образом. Конечно, вы можете добавить несколько экземпляров.

Только учтите, что вероятность получить знак минус перед результатом составляет 50%! Так что можно использовать обертку r.nextLong () в Math.abs (), если вам не нужен знак минус: Long.toString(Math.abs(r.nextLong()), 36);

Ray Hulha 27.01.2013 06:12

@RayHulha: Если вам не нужен знак минус, вы должны его обрезать, потому что, как ни странно, Math.abs возвращает отрицательное значение для Long.MIN_VALUE.

user unknown 27.01.2013 17:28

Интересно, что Math.abs вернул отрицательный результат. Подробнее здесь: bmaurer.blogspot.co.nz/2006/10/…

Phil 11.11.2013 00:34

Проблема с abs решается с помощью побитового оператора для очистки самого значимого бита. Это будет работать для всех значений.

Radiodef 03.04.2018 02:27

@Radiodef По сути, это то, что сказал @userunkown. Я полагаю, вы могли бы также сделать << 1 >>> 1.

shmosel 03.04.2018 02:35

Лучший метод генератора случайных строк

public class RandomStringGenerator{

    private static int randomStringLength = 25 ;
    private static boolean allowSpecialCharacters = true ;
    private static String specialCharacters = "!@$%*-_+:";
    private static boolean allowDuplicates = false ;

    private static boolean isAlphanum = false;
    private static boolean isNumeric = false;
    private static boolean isAlpha = false;
    private static final String alphabet = "abcdefghijklmnopqrstuvwxyz";
    private static boolean mixCase = false;
    private static final String capAlpha = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    private static final String num = "0123456789";

    public static String getRandomString() {
        String returnVal = "";
        int specialCharactersCount = 0;
        int maxspecialCharacters = randomStringLength/4;

        try {
            StringBuffer values = buildList();
            for (int inx = 0; inx < randomStringLength; inx++) {
                int selChar = (int) (Math.random() * (values.length() - 1));
                if (allowSpecialCharacters)
                {
                    if (specialCharacters.indexOf("" + values.charAt(selChar)) > -1)
                    {
                        specialCharactersCount ++;
                        if (specialCharactersCount > maxspecialCharacters)
                        {
                            while (specialCharacters.indexOf("" + values.charAt(selChar)) != -1)
                            {
                                selChar = (int) (Math.random() * (values.length() - 1));
                            }
                        }
                    }
                }
                returnVal += values.charAt(selChar);
                if (!allowDuplicates) {
                    values.deleteCharAt(selChar);
                }
            }
        } catch (Exception e) {
            returnVal = "Error While Processing Values";
        }
        return returnVal;
    }

    private static StringBuffer buildList() {
        StringBuffer list = new StringBuffer(0);
        if (isNumeric || isAlphanum) {
            list.append(num);
        }
        if (isAlpha || isAlphanum) {
            list.append(alphabet);
            if (mixCase) {
                list.append(capAlpha);
            }
        }
        if (allowSpecialCharacters)
        {
            list.append(specialCharacters);
        }
        int currLen = list.length();
        String returnVal = "";
        for (int inx = 0; inx < currLen; inx++) {
            int selChar = (int) (Math.random() * (list.length() - 1));
            returnVal += list.charAt(selChar);
            list.deleteCharAt(selChar);
        }
        list = new StringBuffer(returnVal);
        return list;
    }   

}

Объяснение было бы в порядке.

Peter Mortensen 12.11.2020 18:46

Для этого вы можете использовать библиотеку Apache Commons, RandomStringUtils:

RandomStringUtils.randomAlphanumeric(20).toUpperCase();

Гарантирует ли эта функция создание уникальных результатов при вызове в разное время

kml_ckr 24.09.2012 15:31

@kamil, я посмотрел исходный код RandomStringUtils, и он использует экземпляр java.util.Random, созданный без аргументов. В документации для java.util.Random говорится, что он использует текущее системное время, если не указано семя. Это означает, что его нельзя использовать для идентификаторов / ключей сеанса, поскольку злоумышленник может легко предсказать, какие идентификаторы сеанса сгенерированы в любой момент времени.

Inshallah 26.09.2012 14:14

@Inshallah: Вы (без надобности) чрезмерно изобретаете систему. Хотя я согласен с тем, что в качестве начального числа используется время, злоумышленник должен иметь доступ к следующим данным, чтобы на самом деле получить то, что он хочет. 1. Время с точностью до миллисекунды, когда код был засеян. 2. Количество вызовов, которые произошли до сих пор. 3. Атомарность для его собственного звонка (так что количество звонков на данный момент остается одинаковым). Если у вашего злоумышленника есть все эти три вещи, то перед вами гораздо более серьезная проблема ...

Ajeet Ganga 14.10.2013 03:36

Просто случайно. Вероятность столкновения очень меньше.

manish_s 17.10.2014 20:57

зависимость Gradle: compile 'commons-lang:commons-lang:2.6'

younes0 19.01.2015 17:35

@Ajeet, это неправда. Вы можете получить состояние генератора случайных чисел по его выходным данным. Если злоумышленник может сгенерировать несколько тысяч вызовов для генерации случайных токенов API, он сможет предсказать все будущие токены API.

Thomas Grainger 20.12.2016 16:52

@AjeetGanga Ничего общего с излишней инженерией. Если вы хотите создать идентификаторы сеансов, вам понадобится криптографический генератор псевдослучайных чисел. Каждый запрос, использующий время в качестве начального числа, предсказуем и очень небезопасен для данных, которые должны быть непредсказуемыми. Просто используйте SecureRandom, и все будет хорошо.

Patrick Favre 19.09.2017 13:37

Поскольку commons-lang3.6, RandomStringUtils устарел в пользу RandomStringGenerator из commons-text.

numéro6 17.10.2017 12:31

Вот решение Scala:

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

Объяснение было бы в порядке.

Peter Mortensen 12.11.2020 18:47

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

Объяснение было бы в порядке.

Peter Mortensen 12.11.2020 18:47

Используя библиотеку Apache Commons, это можно сделать одной строкой:

import org.apache.commons.lang.RandomStringUtils;
RandomStringUtils.randomAlphanumeric(64);

Документация

Удивительно, но здесь никто этого не предлагал, но:

import java.util.UUID

UUID.randomUUID().toString();

Легко.

Преимущество этого в том, что UUID красивые, длинные и гарантированно практически невозможно столкнуться с конфликтами.

В Википедии есть хорошее объяснение:

" ...only after generating 1 billion UUIDs every second for the next 100 years, the probability of creating just one duplicate would be about 50%."

Первые четыре бита - это тип версии, а два - вариант, поэтому вы получаете 122 бита случайного числа. Поэтому, если вы используете хотеть, вы можете обрезать его с конца, чтобы уменьшить размер UUID. Это не рекомендуется, но у вас все еще есть много случайности, достаточно для ваших 500 тысяч записей.

Кто-то предложил это, about a year before you.
erickson 10.09.2013 08:49

Вы можете использовать следующий код, если ваш пароль обязательно содержит цифры и буквенные специальные символы:

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

public static String getRandomString(int length)
{
    String randomStr = UUID.randomUUID().toString();
    while(randomStr.length() < length) {
        randomStr += UUID.randomUUID().toString();
    }
    return randomStr.substring(0, length);
}

Это почти то же самое, что и Ответ Стива Маклеода, полученное двумя годами ранее.

erickson 04.10.2013 09:40

В предыдущих ответах много использовалось StringBuilder. Думаю, это просто, но для этого требуется вызов функции для каждого символа, увеличение массива и т. д.

При использовании конструктора строк рекомендуется указать требуемую емкость строки, т. Е.

new StringBuilder(int capacity);

Вот версия, в которой не используются добавление StringBuilder или String и нет словаря.

public static String randomString(int length)
{
    SecureRandom random = new SecureRandom();
    char[] chars = new char[length];
    for(int i=0; i<chars.length; i++)
    {
        int v = random.nextInt(10 + 26 + 26);
        char c;
        if (v < 10)
        {
            c = (char)('0' + v);
        }
        else if (v < 36)
        {
            c = (char)('a' - 10 + v);
        }
        else
        {
            c = (char)('A' - 36 + v);
        }
        chars[i] = c;
    }
    return new String(chars);
}

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

Эта конкатенация строк излишне неэффективна. А безумные отступы делают ваш код почти нечитаемым. Это то же самое, что и Идея Джейми,, но выполняется плохо.

erickson 04.10.2013 09:36

Вы можете создать массив символов, который включает в себя все буквы и цифры, а затем вы можете случайным образом выбрать из этого массива символов и создать свой собственный строковый пароль.

char[] chars = new char[62]; // Sum of letters and numbers

int i = 0;

for(char c = 'a'; c <= 'z'; c++) { // For letters
    chars[i++] = c;
}

for(char c = '0'; c <= '9';c++) { // For numbers
    chars[i++] = c;
}

for(char c = 'A'; c <= 'Z';c++) { // For capital letters
    chars[i++] = c;
}

int numberOfCodes = 0;
String code = "";
while (numberOfCodes < 1) { // Enter how much you want to generate at one time
    int numChars = 8; // Enter how many digits you want in your password

    for(i = 0; i < numChars; i++) {
        char c = chars[(int)(Math.random() * chars.length)];
        code = code + c;
    }
    System.out.println("Code is:" + code);
}

Похоже, это в значительной степени дублирует мой ответ, который был дан двумя годами ранее.

erickson 04.10.2013 09:35

Вы можете использовать класс UUID с его сообщением getLeastSignificantBits (), чтобы получить 64-битные данные случайный, а затем преобразовать их в число с основанием 36 (то есть строку, состоящую из 0-9, A-Z):

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

Это дает нить длиной до 13 символов. Мы используем Math.abs (), чтобы убедиться, что нет знака минус.

Зачем вам использовать UUID для получения случайных битов? Почему бы просто не использовать random.nextLong()? Или даже Double.doubleToLongBits(Math.random())?

erickson 04.10.2013 09:31
  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();
}

Это ничего не добавляет, чего не покрывали десятки ранее приведенных ответов. А создание нового экземпляра Random на каждой итерации цикла неэффективно.

erickson 10.02.2014 09:17

Альтернатива в 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();
}

Это здорово, но если вы хотите, чтобы он был строго буквенно-цифровым (0-9, a-z, A-Z), см. Здесь рациональный java.com/2015/06/…

Dan 23.06.2015 17:08

Еще одно решение ...

public static String generatePassword(int passwordLength) {
    int asciiFirst = 33;
    int asciiLast = 126;
    Integer[] exceptions = { 34, 39, 96 };

    List<Integer> exceptionsList = Arrays.asList(exceptions);
    SecureRandom random = new SecureRandom();
    StringBuilder builder = new StringBuilder();
    for (int i=0; i<passwordLength; i++) {
        int charIndex;

        do {
            charIndex = random.nextInt(asciiLast - asciiFirst + 1) + asciiFirst;
        }
        while (exceptionsList.contains(charIndex));

        builder.append((char) charIndex);
    }
    return builder.toString();
}

Использование UUID небезопасно, потому что части UUID вовсе не случайны. процедура эриксона очень удобен, но он не создает строки одинаковой длины. Следующего фрагмента должно быть достаточно:

/*
 * 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 и символы a-z, нам потребуется случайное число от 0 до 35, чтобы получить по одному каждому символу.

BigInteger предоставляет конструктор для генерации случайного числа, равномерно распределенного в диапазоне 0 to (2^numBits - 1). К сожалению, 35 - это не число, которое можно получить с помощью 2 ^ numBits - 1.

Итак, у нас есть два варианта: либо использовать 2^5-1=31, либо 2^6-1=63. Если бы мы выбрали 2^6, то получили бы много «ненужных» / «длинных» номеров. Поэтому 2^5 - лучший вариант, даже если мы потеряем четыре символа (w-z). Теперь, чтобы сгенерировать строку определенной длины, мы можем просто использовать число 2^(length*numBits)-1. Последняя проблема: если нам нужна строка определенной длины, random может сгенерировать небольшое число, поэтому длина не соблюдается, поэтому мы должны дополнить строку до требуемой длины, добавив нули.

не могли бы вы лучше объяснить 5?

Julian Suarez 09.03.2016 19:56

Может быть, это полезно

package password.generater;

import java.util.Random;

/**
 *
 * @author dell
 */
public class PasswordGenerater {

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        int length= 11;
        System.out.println(generatePswd(length));

        // TODO code application logic here
    }
    static char[] generatePswd(int len){
        System.out.println("Your Password ");
        String charsCaps = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; 
        String Chars = "abcdefghijklmnopqrstuvwxyz";
        String nums = "0123456789";
        String symbols = "!@#$%^&*()_+-=.,/';:?><~*/-+";
        String passSymbols=charsCaps + Chars + nums +symbols;
        Random rnd=new Random();
        char[] password=new char[len];

        for(int i=0; i<len;i++){
            password[i]=passSymbols.charAt(rnd.nextInt(passSymbols.length()));
        }
      return password;

    }
}

Вот однострочный текст от 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 '-'

Это легко достижимо без каких-либо внешних библиотек.

1. Генерация криптографических псевдослучайных данных (PRNG)

Для начала вам понадобится криптографический ГПСЧ. Java имеет для этого SecureRandom и обычно использует лучший источник энтропии на машине (например, /dev/random). Подробнее читайте здесь.

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

Примечание:SecureRandom - самый медленный, но самый безопасный способ генерации случайных байтов в Java. Однако я рекомендую нет учитывать производительность здесь, поскольку обычно он не оказывает реального влияния на ваше приложение, если вам не нужно генерировать миллионы токенов в секунду.

2. Требуемое пространство возможных значений

Затем вам нужно решить, насколько уникальным должен быть ваш токен. Весь и единственный смысл рассмотрения энтропии состоит в том, чтобы убедиться, что система может противостоять атакам грубой силы: пространство возможных значений должно быть настолько большим, чтобы любой злоумышленник мог попробовать только незначительную часть значений за несмешное время 1.

Уникальные идентификаторы, такие как random UUID, имеют 122 бита энтропии (т. Е. 2 ​​^ 122 = 5,3x10 ^ 36) - вероятность коллизии равна «* (...), так как вероятность дублирования составляет один на миллиард, 103 должен быть сгенерирован триллион UUID версии 4 2 ". Мы выберем 128 бит, так как он умещается ровно в 16 байт. и рассматривается как весьма достаточный, поскольку он уникален практически для всех, но в самых крайних случаях использования, и вам не нужно думать о дубликатах. Вот простая сравнительная таблица энтропии, включая простой анализ проблема дня рождения.

Comparison of token sizes

For simple requirements, 8 or 12 byte length might suffice, but with 16 bytes you are on the "safe side".

И это в основном все. Последнее, что нужно подумать о кодировании, чтобы его можно было представить в виде текста для печати (читай 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 байтов создают строку длиной 32). Поэтому шестнадцатеричный формат менее эффективен, чем Base32, но его можно использовать в большинстве случаев (URL), поскольку он использует только 0-9 и A - F. Пример кодирования 16 байт: 4fa3dd0f57cb3bf331441ed285b27735. См. Обсуждение Stack Overflow о преобразовании в шестнадцатеричный формат здесь..

Дополнительные кодировки, такие как Base85 и экзотический Base122, существуют с лучшей / худшей эффективностью использования пространства. Вы можете создать свою собственную кодировку (что в основном делает большинство ответов в этом потоке), но я бы не советовал этого делать, если у вас нет очень конкретных требований. См. больше схем кодирования в статье Википедии.

4. Резюме и пример

  • Используйте SecureRandom
  • Используйте не менее 16 байтов (2 ^ 128) возможных значений
  • Кодируйте в соответствии с вашими требованиями (обычно hex или base32, если вам нужно, чтобы он был буквенно-цифровым)

Не надо

  • ... используйте кодировку домашнего пива: лучше обслуживаемый и читаемый для других, если они видят, какую стандартную кодировку вы используете вместо странных циклов за, создающих символы за раз.
  • ... использовать UUID: нет никаких гарантий случайности; вы тратите 6 бит энтропии и имеете подробное строковое представление

Пример: Генератор шестнадцатеричных токенов

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

//generateRandomHexToken(16) -> 2189df7475e96aa3982dbeab266497cd

Пример: генератор токенов Base64 (безопасный для URL)

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 Tool

Если вам нужен готовый инструмент CLI, вы можете использовать игральная кость:

Пример: Связанная проблема - Защитите свои текущие идентификаторы

Если у вас уже есть идентификатор, который вы можете использовать (например, синтетический long в вашей сущности), но не хочу публиковать внутреннюю ценность, вы можете использовать эту библиотеку для его шифрования и обфускации: 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);

Этот ответ является полным и работает без добавления каких-либо зависимостей. Если вы хотите избежать возможных знаков минус в выводе, вы можете предотвратить отрицательные значения BigInteger, используя параметр конструктора: BigInteger(1, token) вместо BigInteger(token).

francoisr 11.07.2017 10:50

Танки @francoisr за подсказку, отредактировал пример кода

Patrick Favre 11.07.2017 11:38

import java.security.SecureRandom; и import java.math.BigInteger; необходимы для того, чтобы пример работал, но он отлично работает!

anothermh 04.10.2018 04:45

Хороший ответ, но / dev / random - это метод блокировки, который является причиной медленной до точки блокировки, если энтропия слишком низкая. Лучшим и неблокирующим методом является / dev / urandom. Это можно настроить с помощью <jre> /lib/security/java.security и установить securerandom.source = file: / dev /./ urandom

Muzammil 10.05.2020 19:27

@Muzammil См. tersesystems.com/blog/2015/12/17/… (также ссылка на ответ) - new SecureRandom() использует /dev/urandom

Patrick Favre 11.05.2020 11:25

Кроме того, вы можете генерировать любые строчные или прописные буквы или даже специальные символы с помощью данных из таблицы ASCII. Например, сгенерируйте буквы в верхнем регистре от A (DEC 65) до Z (DEC 90):

String generateRandomStr(int min, int max, int size) {
    String result = "";
    for (int i = 0; i < size; i++) {
        result += String.valueOf((char)(new Random().nextInt((max - min) + 1) + min));
    }
    return result;
}

Сгенерированный вывод для generateRandomStr(65, 90, 100));:

TVLPFQJCYFXQDCQSLKUKKILKKHAUFYEXLUQFHDWNMRBIRRRWNXNNZQTINZPCTKLHGHVYWRKEOYNSOFPZBGEECFMCOKWHLHCEWLDZ

Мне не очень нравятся эти ответы относительно "простого" решения: S

Я бы выбрал простую;), чистую Java, один лайнер (энтропия основана на случайной длине строки и заданном наборе символов):

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")); // The character set 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")); // The character set can basically be anything
    }
}

Но с другой стороны, вы также можете использовать UUID, который имеет довольно хорошую энтропию:

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

Вот простой однострочник, использующий UUID в качестве базы символов и возможность указать (почти) любую длину. (Да, я знаю, что использование UUID предлагалось раньше.)

public static String randString(int length) {
    return UUID.randomUUID().toString().replace("-", "").substring(0, Math.min(length, 32)) + (length > 32 ? randString(length - 32) : "");
}

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

Действительно мило! Но в цикле for должен быть length вместо chars.length: for (int i = 0; i < length; i++)

Incinerator 29.08.2019 16:19

Я думаю, что это самое маленькое решение здесь или почти одно из самых маленьких:

 public String generateRandomString(int length) {
    String randomString = "";

    final char[] chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz01234567890".toCharArray();
    final Random random = new Random();
    for (int i = 0; i < length; i++) {
        randomString = randomString + chars[random.nextInt(chars.length)];
    }

    return randomString;
}

Код работает нормально. Если вы используете этот метод, я рекомендую вам использовать более 10 символов. Коллизия происходит при 5 символах / 30362 итерациях. Это заняло 9 секунд.

Вот решение Java 8 на основе потоков.

    public String generateString(String alphabet, int length) {
        return generateString(alphabet, length, new SecureRandom()::nextInt);
    }

    // nextInt = bound -> n in [0, bound)
    public String generateString(String source, int length, IntFunction<Integer> nextInt) {
        StringBuilder sb = new StringBuilder();
        IntStream.generate(source::length)
                .boxed()
                .limit(length)
                .map(nextInt::apply)
                .map(source::charAt)
                .forEach(sb::append);

        return sb.toString();
    }

Используйте это как

String alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
int length = 12;
String generated = generateString(alphabet, length);
System.out.println(generated);

Функция nextInt должна принимать int bound и возвращать случайное число от 0 до bound - 1.

Эффективно и коротко.

/**
 * Utility class for generating random Strings.
 */
public interface RandomUtil {

    int    DEF_COUNT = 20;
    Random RANDOM    = new SecureRandom();

    /**
     * Generate a password.
     *
     * @return the generated password
     */
    static String generatePassword() {
        return generate(true, true);
    }

    /**
     * Generate an activation key.
     *
     * @return the generated activation key
     */
    static String generateActivationKey() {
        return generate(false, true);
    }

    /**
     * Generate a reset key.
     *
     * @return the generated reset key
     */
    static String generateResetKey() {
        return generate(false, true);
    }

    static String generate(boolean letters, boolean numbers) {
        int
            start = ' ',
            end   = 'z' + 1,
            count = DEF_COUNT,
            gap   = end - start;
        StringBuilder builder = new StringBuilder(count);

        while (count-- != 0) {
            int codePoint = RANDOM.nextInt(gap) + start;

            switch (getType(codePoint)) {
                case UNASSIGNED:
                case PRIVATE_USE:
                case SURROGATE:
                    count++;
                    continue;
            }

            int numberOfChars = charCount(codePoint);

            if (count == 0 && numberOfChars > 1) {
                count++;
                continue;
            }

            if (letters && isLetter(codePoint)
                || numbers && isDigit(codePoint)
                || !letters && !numbers) {

                builder.appendCodePoint(codePoint);
                if (numberOfChars == 2)
                    count--;
            }
            else
                count++;
        }
        return builder.toString();
    }
}

public static String RandomAlphanum(int length)
{
    String charstring = "abcdefghijklmnopqrstuvwxyz0123456789";
    String randalphanum = "";
    double randroll;
    String randchar;
    for (double i = 0; i < length; i++)
    {
        randroll = Math.random();
        randchar = "";
        for (int j = 1; j <= 35; j++)
        {
            if (randroll <= (1.0 / 36.0 * j))
            {
                randchar = Character.toString(charstring.charAt(j - 1));
                break;
            }
        }
        randalphanum += randchar;
    }
    return randalphanum;
}

Я использовал очень примитивный алгоритм с использованием Math.random (). Чтобы увеличить случайность, вы можете напрямую реализовать класс util.Date. Тем не менее, это работает.

Я использую библиотеку из Apache Commons для создания буквенно-цифровой строки:

import org.apache.commons.lang3.RandomStringUtils;

String keyLength = 20;
RandomStringUtils.randomAlphanumeric(keylength);

Это быстро и просто!

Прекрасно работает! и у него также есть случайный случай, который мне нужен.

user2677034 13.05.2020 05:32

Другие вопросы по теме