จะสร้างสตริงตัวอักษรและตัวเลขแบบสุ่มได้อย่างไร


1741

ฉันกำลังมองหาอัลกอริทึม Java ง่ายๆในการสร้างสตริงตัวอักษรและตัวเลขแบบเทียมหลอก ในสถานการณ์ของฉันมันจะถูกใช้เป็นเซสชั่น / ตัวระบุคีย์ที่ไม่ซ้ำใคร500K+รุ่นหนึ่ง (ความต้องการของฉันไม่ต้องการอะไรที่ซับซ้อนกว่านี้)

เป็นการดีที่ฉันจะสามารถระบุความยาวขึ้นอยู่กับความต้องการเฉพาะของฉัน ตัวอย่างเช่นสตริงที่มีความยาว 12 ที่สร้างขึ้นอาจมีลักษณะคล้าย"AEYGF7K0DM1X"กัน


150
ระวังความขัดแย้งวันเกิด
pablosaraiva

58
แม้จะพิจารณาถึงความขัดแย้งในวันเกิดถ้าคุณใช้ตัวอักษรและตัวเลข 12 ตัว (รวม 62 ตัว) คุณก็ยังคงต้องการสตริงมากกว่า 34,000 ล้านสตริงในการเข้าถึงความขัดแย้ง และบุคคลที่เกิดวันเกิดไม่ได้รับประกันการชนกัน แต่มันบอกว่ามันมีโอกาสมากกว่า 50%
NullUserException

4
@NullUserException โอกาสสำเร็จ 50% (ต่อการลอง) นั้นสูงมาก: แม้จะมี 10 ครั้งก็ตามอัตราความสำเร็จเท่ากับ 0.999 ด้วยสิ่งนี้และความจริงที่ว่าคุณสามารถทดลองใช้ A LOT ได้ภายในเวลา 24 ชั่วโมงคุณไม่จำเป็นต้องมี 34 พันล้านสายเพื่อให้แน่ใจว่าจะเดาได้อย่างน้อยหนึ่งข้อ นั่นคือเหตุผลที่โทเค็นการเซสชันบางอย่างควรจะยาวจริงๆ
Pijusn

16
เหล่านี้รหัสบรรทัดเดียว 3 มีประโยชน์อย่างมากคิดว่าผม ..Long.toHexString(Double.doubleToLongBits(Math.random())); UUID.randomUUID().toString(); RandomStringUtils.randomAlphanumeric(12);
Manindar

18
@Pijusn ฉันรู้ว่ามันเก่า แต่ ... "โอกาส 50%" ในวันเกิดความขัดแย้งไม่ใช่ "ต่อการลอง" มันเป็น "50% โอกาสที่ออกจาก (ในกรณีนี้) 34 พันล้านสายมีอยู่ที่ ซ้ำกันอย่างน้อยหนึ่งคู่ " คุณต้องการ 1.6 sept illion - 1.6e21 - รายการในฐานข้อมูลของคุณเพื่อให้มีโอกาส 50% ต่อการลอง
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 ในรูปแบบมาตรฐานใช้พื้นที่มาก: 36 ตัวอักษรสำหรับ entropy เพียง 122 บิต (ไม่ใช่ทุกบิตของ UUID ที่ "สุ่ม" จะถูกเลือกแบบสุ่ม) สตริงตัวอักษรและตัวเลขที่สุ่มเลือกนั้นจะบรรจุเอนโทรปีมากขึ้นด้วยอักขระเพียง 21 ตัว

UUID ไม่ยืดหยุ่น พวกเขามีโครงสร้างมาตรฐานและเค้าโครง นี่คือคุณธรรมหลักของพวกเขาเช่นเดียวกับจุดอ่อนหลักของพวกเขา เมื่อร่วมมือกับบุคคลภายนอกมาตรฐานที่เสนอโดย UUID อาจมีประโยชน์ สำหรับการใช้ภายในอย่างหมดจดพวกเขาสามารถไม่มีประสิทธิภาพ


6
หากคุณต้องการช่องว่างในของคุณคุณสามารถตรึง.replaceAll("\\d", " ");ลงบนจุดสิ้นสุดของ return new BigInteger(130, random).toString(32);บรรทัดเพื่อทำการแลกเปลี่ยน regex มันแทนที่ตัวเลขทั้งหมดด้วยช่องว่าง ใช้งานได้ดีสำหรับฉัน: ฉันใช้สิ่งนี้เป็นสิ่งทดแทนสำหรับ Lorem Ipsum ส่วนหน้า
weisjohn

4
@weisjohn นั่นเป็นความคิดที่ดี คุณสามารถทำสิ่งที่คล้ายกับวิธีที่สองโดยการลบตัวเลขออกจากsymbolsและใช้ช่องว่างแทน คุณสามารถควบคุมความยาว "คำ" โดยเฉลี่ยโดยการเปลี่ยนจำนวนช่องว่างในสัญลักษณ์ (เกิดขึ้นอีกสำหรับคำที่สั้นกว่า) สำหรับโซลูชันข้อความปลอมที่เหนือชั้นคุณสามารถใช้โซ่มาร์คอฟได้!
erickson

4
ตัวระบุเหล่านี้จะถูกสุ่มเลือกจากพื้นที่ขนาดที่แน่นอน พวกเขาอาจมีความยาว 1 ตัวอักษร หากคุณต้องการความยาวคงที่คุณสามารถใช้วิธีที่สองพร้อมกับSecureRandomอินสแตนซ์ที่กำหนดให้กับrandomตัวแปร
erickson

15
ทำไม. toString (32) มากกว่า. toString (36)
ejain

17
@ejain เพราะ 32 = 2 ^ 5; อักขระแต่ละตัวจะแสดงถึง 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
ระวังว่าวิธีนี้จะสร้างสตริงแบบสุ่มที่มีอักขระฐานสิบหกเท่านั้น ซึ่งสามารถปรับได้ในบางกรณี
เดฟ

5
คลาส UUID มีประโยชน์ อย่างไรก็ตามมันไม่ได้กะทัดรัดเท่ากับตัวระบุที่ผลิตโดยคำตอบของฉัน นี่อาจเป็นปัญหาตัวอย่างเช่นใน URL ขึ้นอยู่กับความต้องการของคุณ
erickson

6
@Ruggs - เป้าหมายคือสตริงตัวเลขและตัวอักษร การขยายเอาต์พุตไปยังไบต์ใด ๆ ที่เป็นไปได้นั้นเหมาะสมกับสิ่งนั้นอย่างไร
erickson

72
ตาม RFC4122 โดยใช้ UUID เป็นโทเค็นเป็นความคิดที่ดี: อย่าคิดว่า 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 โซลูชันที่ง่ายที่สุดที่นี่เพื่อสร้างสตริงแบบสุ่มของความยาวที่ระบุ (นอกเหนือจากการใช้ RandomStringUtils จาก Commons Lang)
Jonik

12
พิจารณาใช้SecureRandomแทนRandomชั้นเรียน หากรหัสผ่านถูกสร้างขึ้นบนเซิร์ฟเวอร์มันอาจเสี่ยงต่อการโจมตีตามกำหนดเวลา
foens

8
ฉันจะเพิ่มตัวพิมพ์เล็กด้วย: AB = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";และตัวอักษรอื่นที่อนุญาต
ACV

1
ทำไมไม่ลองstatic Random rnd = new Random();ใช้วิธีนี้ดูล่ะ?
Micro

4
@MicroR มีเหตุผลที่ดีในการสร้างRandomวัตถุในแต่ละวิธีการภาวนาหรือไม่? ฉันไม่คิดอย่างนั้น
cassiomolin

484

หากคุณยินดีที่จะใช้คลาส Apache คุณสามารถใช้org.apache.commons.text.RandomStringGenerator(คอมมอน - ข้อความ)

ตัวอย่าง:

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
ตรวจสอบให้แน่ใจว่าคุณใช้ 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

ไม่ได้ใช้. สิ่งนี้จะสร้างลำดับที่ไม่ปลอดภัย !
Patrick Favre

110

คุณสามารถใช้ไลบรารี Apache สำหรับสิ่งนี้: RandomStringUtils

RandomStringUtils.randomAlphanumeric(20).toUpperCase();

18
@kamil ฉันดูซอร์สโค้ดของ RandomStringUtils และใช้อินสแตนซ์ของ java.util.Random instantiated โดยไม่มีข้อโต้แย้ง เอกสารสำหรับ java.util.Random กล่าวว่ามันใช้เวลาระบบปัจจุบันหากไม่มีการจัดหาเมล็ด ซึ่งหมายความว่าไม่สามารถใช้สำหรับตัวระบุเซสชัน / คีย์ได้เนื่องจากผู้โจมตีสามารถคาดการณ์ได้อย่างง่ายดายว่าตัวระบุเซสชันที่สร้างขึ้นนั้นเป็นอย่างไรในเวลาใดก็ตาม
Inshallah

36
@Inshallah: คุณ (กำลังทำระบบ) โดยไม่จำเป็น ในขณะที่ฉันยอมรับว่ามันใช้เวลาเป็นเมล็ดพันธุ์ผู้โจมตีจะต้องเข้าถึงข้อมูลต่อไปนี้เพื่อให้ได้สิ่งที่เขาต้องการจริง ๆ 1. ใช้เวลาเป็นมิลลิวินาทีที่แน่นอนเมื่อโค้ดถูก seed 2. จำนวนการโทรที่เกิดขึ้น 3. Atomicity สำหรับการโทรของเขา (ดังนั้นจำนวน ram-so-far ram ที่เหมือนกัน) หากผู้โจมตีของคุณมีทั้งสามสิ่งนี้แสดงว่าคุณมีปัญหาที่ใหญ่กว่ามาก ...
Ajeet Ganga

3
การพึ่งพา gradle: compile 'commons-lang:commons-lang:2.6'
คุณ

4
@Ajeet นี้ไม่เป็นความจริง คุณสามารถได้รับสถานะของตัวสร้างตัวเลขสุ่มจากเอาท์พุท หากผู้โจมตีสามารถสร้างการโทรไม่กี่พันครั้งเพื่อสร้างโทเค็น API แบบสุ่มผู้โจมตีจะสามารถทำนายโทเค็น API ในอนาคตทั้งหมดได้
Thomas Grainger

3
@AjeetGanga ไม่มีส่วนเกี่ยวข้องกับงานวิศวกรรม หากคุณต้องการสร้างรหัสเซสชันคุณต้องมีเครื่องกำเนิดไฟฟ้าแบบหลอกเข้ารหัส ทุก prng ใช้เวลาเป็นเมล็ดสามารถคาดการณ์และไม่ปลอดภัยมากสำหรับข้อมูลที่ไม่สามารถคาดการณ์ได้ เพียงแค่ใช้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
มันช่วยฉันเหมือนกัน แต่เป็นเลขฐานสิบหกเท่านั้น :(
noquery

@ Zippoxer คุณสามารถต่อว่าหลายต่อหลายครั้ง =)
daniel.bavrin

7
ตัวอย่างของ OP แสดงสตริงต่อไปนี้เป็นตัวอย่างAEYGF7K0DM1Xซึ่งไม่ใช่เลขฐานสิบหก มันทำให้ฉันเป็นกังวลว่าผู้คนมักเข้าใจผิดว่าตัวอักษรและตัวเลขด้วยเลขฐานสิบหก พวกเขาไม่เหมือนกัน
hfontanez

6
นี่คือการสุ่มน้อยกว่าที่ควรจะได้รับความยาวของสตริงที่Math.random()สร้างdoubleระหว่าง 0 และ 1 ดังนั้นส่วนเลขชี้กำลังส่วนใหญ่จะไม่ได้ใช้ ใช้random.nextLongสำหรับการสุ่มlongแทนแฮ็กที่น่าเกลียดนี้
maaartinus

80

สามารถทำได้โดยไม่ต้องมีห้องสมุดภายนอก

1. Cryptographic Pseudo Random Data Generation

ก่อนอื่นคุณต้องมี 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) - โอกาสที่จะเกิดการชนคือ "* (... ) เพื่อให้มีโอกาสหนึ่งพันล้านครั้งในการทำซ้ำ 103 ล้านล้านรุ่น 4 ต้องสร้าง UUID 2 " เราจะเลือก 128 บิตเนื่องจากมันพอดีกับ 16 ไบต์และถือว่ามีความเพียงพอสูงเพราะมันมีความพิเศษสำหรับทุก ๆ แต่ที่สำคัญที่สุดใช้เคสและคุณไม่ต้องคิดซ้ำ นี่คือตารางเปรียบเทียบเอนโทรปีที่เรียบง่ายรวมถึงการวิเคราะห์ปัญหาวันเกิดอย่างง่าย

การเปรียบเทียบขนาดโทเค็น

สำหรับข้อกำหนดอย่างง่ายความยาว 8 หรือ 12 ไบต์อาจพอเพียง แต่ด้วย 16 ไบต์คุณจะอยู่ใน "ด้านที่ปลอดภัย"

และนั่นเป็นพื้น สิ่งสุดท้ายคือการคิดเกี่ยวกับการเข้ารหัสเพื่อให้สามารถแสดงเป็นข้อความที่พิมพ์ได้ (อ่าน, a String)

3. การเข้ารหัสไบนารี่เป็นข้อความ

การเข้ารหัสทั่วไปรวมถึง:

  • Base64ตัวละครทุกตัวเข้ารหัส 6 บิตสร้างค่าใช้จ่าย 33% โชคดีที่มีการใช้งานที่เป็นมาตรฐานในJava 8+และAndroid ด้วย Java เก่าคุณสามารถใช้ใด ๆ ของหลายห้องสมุดของบุคคลที่สาม หากคุณต้องการให้โทเค็นของคุณปลอดภัย url ให้ใช้RFC4648 เวอร์ชันurl-safe (ซึ่งโดยทั่วไปจะรองรับการใช้งานส่วนใหญ่) ตัวอย่างการเข้ารหัส 16 ไบต์พร้อมการขยาย:XfJhfv3C0P6ag7y9VQxSbw==

  • Base32ตัวละครทุกตัวเข้ารหัส 5 บิตสร้างค่าใช้จ่าย 40% สิ่งนี้จะใช้A-Zและ2-7ทำให้มีพื้นที่ว่างพอสมควรขณะที่เป็นตัวอักษรและตัวเลข ไม่มีคือการดำเนินการตามมาตรฐานใน JDK ตัวอย่างการเข้ารหัส 16 ไบต์โดยไม่มีการขยาย:WUPIL5DQTZGMF4D3NX5L7LNFOY

  • Base16(ฐานสิบหก) อักขระทุกตัวเข้ารหัส 4 บิตที่ต้องการ 2 อักขระต่อไบต์ (เช่น 16 ไบต์สร้างสตริงที่มีความยาว 32) ดังนั้น hex จึงมีพื้นที่น้อยกว่าBase32แต่มีความปลอดภัยในการใช้งานในกรณีส่วนใหญ่ (url) เนื่องจากมีการใช้0-9และAเพื่อFเท่านั้น ตัวอย่างการเข้ารหัส 16 4fa3dd0f57cb3bf331441ed285b27735ไบต์: ดูการสนทนา SO เกี่ยวกับการแปลงเป็น hex ที่นี่

การเข้ารหัสเพิ่มเติมเช่นBase85และแปลกใหม่Base122อยู่กับประสิทธิภาพ / พื้นที่ดีขึ้นแย่ลง คุณสามารถสร้างการเข้ารหัสของคุณเอง (ซึ่งโดยทั่วไปคำตอบส่วนใหญ่ในกระทู้นี้จะทำ) แต่ฉันจะแนะนำกับมันถ้าคุณไม่มีข้อกำหนดที่เฉพาะเจาะจงมาก ดูรูปแบบการเข้ารหัสเพิ่มเติมในบทความ Wikipedia

4. สรุปและตัวอย่าง

  • ใช้ SecureRandom
  • ใช้ค่าที่เป็นไปได้อย่างน้อย 16 ไบต์ (2 ^ 128)
  • เข้ารหัสตามความต้องการของคุณ (โดยปกติhexหรือbase32หากคุณต้องการให้เป็นตัวเลขและตัวอักษร)

อย่า

  • ... ใช้การเข้ารหัสการชงที่บ้านของคุณ: บำรุงรักษาได้ดีกว่าและสามารถอ่านได้สำหรับผู้อื่นหากพวกเขาเห็นการเข้ารหัสมาตรฐานที่คุณใช้แทนที่จะเป็นเรื่องแปลกสำหรับลูปในการสร้างตัวอักษรในแต่ละครั้ง
  • ... ใช้ UUID: ไม่มีการรับประกันแบบสุ่ม คุณกำลังเสียเอนโทรปี 6 บิตและมีการแทนค่าสตริงแบบละเอียด

ตัวอย่าง: Hex Token Generator

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

ตัวอย่าง: เครื่องกำเนิดโทเค็น 64 (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 Tool

หากคุณต้องการเครื่องมือ cli ที่พร้อมใช้งานคุณสามารถใช้ลูกเต๋า: https://github.com/patrickfav/dice

ตัวอย่าง: ปัญหาที่เกี่ยวข้อง - ป้องกันรหัสปัจจุบันของคุณ

หากคุณมีรหัสที่คุณสามารถใช้ได้ (เช่นสังเคราะห์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
Patrick Favre

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
การสร้าง int แบบสุ่มที่มีการดับเบิ้ลนี้ถูกทำลายโดยการออกแบบช้าและอ่านไม่ได้ ใช้หรือRandom#nextInt nextLongเปลี่ยนเป็นSecureRandomหากจำเป็น
maaartinus

31

น่าแปลกใจที่ไม่มีใครได้แนะนำที่นี่ แต่:

import java.util.UUID

UUID.randomUUID().toString();

ง่าย.

ประโยชน์ของสิ่งนี้คือ UUID นั้นดีและยาวและรับประกันว่าจะเป็นไปไม่ได้ที่จะชนกัน

Wikipedia มีคำอธิบายที่ดีเกี่ยวกับมัน:

"... หลังจากสร้าง UUID เพียง 1 พันล้านทุก ๆ วินาทีในอีก 100 ปีข้างหน้าความน่าจะเป็นที่จะสร้างซ้ำเพียงครั้งเดียวจะอยู่ที่ประมาณ 50%"

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

4 บิตแรกเป็นประเภทรุ่นและ 2 สำหรับตัวแปรดังนั้นคุณจะได้รับ 122 บิตของการสุ่ม ดังนั้นหากคุณต้องการคุณสามารถตัดปลายจากปลายเพื่อลดขนาดของ UUID ไม่แนะนำ แต่คุณยังมีการสุ่มจำนวนมากเพียงพอสำหรับการบันทึก 500k ของคุณง่าย


39
มีคนแนะนำให้คุณประมาณหนึ่งปีก่อนคุณ
erickson

31

โซลูชันสั้นและง่าย แต่ใช้ตัวพิมพ์เล็กและตัวเลขเท่านั้น:

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

ขนาดนั้นประมาณ 12 หลักไปจนถึงฐาน 36 และไม่สามารถปรับปรุงได้อีก แน่นอนคุณสามารถต่อท้ายหลายอินสแตนซ์ได้


11
เพียงจำไว้ว่ามีโอกาส 50% ของเครื่องหมายลบหน้าผล! ดังนั้นการห่อ r.nextLong () ใน Math.abs () สามารถใช้ได้ถ้าคุณไม่ต้องการเครื่องหมายลบ: 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) ดูที่นี่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 to (2^numBits - 1)แต่น่าเสียดายที่ 35 เป็นจำนวนไม่ซึ่งสามารถได้รับจาก 2 ^ numBits - 1 ดังนั้นเราจึงมีสองตัวเลือก: ทั้งสองไปด้วยหรือ2^5-1=31 2^6-1=63หากเราจะเลือก2^6เราจะได้รับหมายเลข "ไม่แน่นอน" / "อีกต่อไป" จำนวนมาก ดังนั้น2^5เป็นตัวเลือกที่ดีกว่าแม้ว่าเราจะหลวมตัวละคร 4 ตัว (wz) ในการสร้างสตริงที่มีความยาวเราสามารถใช้ a2^(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บ่อยครั้ง ทำไมคุณถึงแยกการประกาศของ spot และ initialisxation? อะไรคือข้อได้เปรียบของดัชนี 1,2,3,4 แทนที่จะเป็น 0,1,2,3 สิ่งสำคัญที่สุด: คุณใช้ค่าสุ่มและเปรียบเทียบกับ if-else 4 เท่าของค่าใหม่ซึ่งอาจไม่ตรงกันเสมอโดยไม่ได้รับการสุ่มมากขึ้น แต่อย่าลังเลที่จะย้อนกลับ
ผู้ใช้ที่ไม่รู้จัก

8

ฉันพบโซลูชันนี้ที่สร้างสตริงที่เข้ารหัส hex แบบสุ่ม การทดสอบหน่วยที่ให้มาดูเหมือนว่าจะขึ้นอยู่กับกรณีการใช้งานหลักของฉัน แม้ว่ามันจะซับซ้อนกว่าคำตอบอื่น ๆ เล็กน้อย

/**
 * 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

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

ไม่ชอบคำตอบใด ๆ เกี่ยวกับวิธีแก้ปัญหา "แบบง่าย": 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"));//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ใน Unix และสามารถกำหนดค่าได้มาก


ขอบคุณแก้ไข ดังนั้นอย่างน้อยก็มีแหล่งที่มาและการเชื่อมโยงที่ถูกต้อง ข้อเสียดูเหมือนว่าจะไม่ได้รับการปรับปรุงในขณะที่แม้ว่าฉันเห็น pwgen ได้รับการปรับปรุงอย่างเป็นธรรมเมื่อเร็ว ๆ นี้
michaelok

4

คุณสามารถใช้คลาส UUID กับข้อความ getLeastSignentialBits () เพื่อรับข้อมูล 64 บิตแบบสุ่มจากนั้นแปลงเป็นหมายเลข Radix 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
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
การต่อสตริงนั้นไม่มีประสิทธิภาพโดยไม่จำเป็น และการเยื้องอย่างบ้าคลั่งทำให้โค้ดของคุณอ่านไม่ได้เกือบ นี่เป็นความคิดเดียวกับของเจมี่แต่ทำงานได้ไม่ดี
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
ดีจริงๆ! แต่ควรlengthแทนที่ด้วยchars.lengthการวนซ้ำ: for (int i = 0; i < length; i++)
เตาเผาขยะ

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.