Java生成安全随机密码

本文聚焦Java中生成安全随机密码的多种方法,约定安全密码含10个字符,需包含特定数量的大小写字母、数字和特殊字符。介绍了使用Passay、RandomStringGenerator、RandomStringUtils以及自定义方法来生成密码,还提及各方法的特点、限制和注意事项。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Java生成安全随机密码

本文讨论在Java中使用多种方法生成安全随机密码。我们示例中约定安全密码包括10个字符,至少包括两个小写字母、两个大写字母、两个数字以及两个特殊字符。

1. 使用Passay

Passay是非常强大的密码策略工具库,我们可以利用其生成符合特定规则的密码,读者可以参考上篇博文。通过使用CharacterData 缺省实现,可以规划密码规则,当然也能根据需要自定义CharacterData 。

public String generatePassayPassword() {
    PasswordGenerator gen = new PasswordGenerator();
    CharacterData lowerCaseChars = EnglishCharacterData.LowerCase;
    CharacterRule lowerCaseRule = new CharacterRule(lowerCaseChars);
    lowerCaseRule.setNumberOfCharacters(2);
 
    CharacterData upperCaseChars = EnglishCharacterData.UpperCase;
    CharacterRule upperCaseRule = new CharacterRule(upperCaseChars);
    upperCaseRule.setNumberOfCharacters(2);
 
    CharacterData digitChars = EnglishCharacterData.Digit;
    CharacterRule digitRule = new CharacterRule(digitChars);
    digitRule.setNumberOfCharacters(2);
 
    CharacterData specialChars = new CharacterData() {
        public String getErrorCode() {
            return ERROR_CODE;
        }
 
        public String getCharacters() {
            return "!@#$%^&*()_+";
        }
    };
    CharacterRule splCharRule = new CharacterRule(specialChars);
    splCharRule.setNumberOfCharacters(2);
 
    String password = gen.generatePassword(10, splCharRule, lowerCaseRule, 
      upperCaseRule, digitRule);
    return password;
}

这里我们自定义了CharacterData指定特殊字符,可以现在一组有效特殊字符。另外对于其他规则利用缺省CharacterData实现。

下面通过单元测试检查生成的密码,示例中检查存在两个特殊字符。

@Test
public void whenPasswordGeneratedUsingPassay_thenSuccessful() {
    RandomPasswordGenerator passGen = new RandomPasswordGenerator();
    String password = passGen.generatePassayPassword();
    int specialCharCount = 0;
    for (char c : password.toCharArray()) {
        if (c >= 33 || c <= 47) {
            specialCharCount++;
        }
    }
    assertTrue("Password validation failed in Passay", specialCharCount >= 2);
}

值得注意的是,尽管Passay是开源的,但它同时获得了LGPL和Apache 2的双重许可。与任何第三方软件一样,当我们在产品中使用它时,我们必须确保遵守这些许可。LGPL许可不修改其源码情况下不受限制,一旦修改源码则必须也是LGPL许可。

2. 使用 RandomStringGenerator

接下来,我们看看Apache Commons Text库中的RandomStringGenerator 类。通过它能生成特定数量的Unicode字符串。增加依赖:

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-text</artifactId>
    <version>1.4</version>
</dependency>

通过使用RandomStringGenerator.Builder 创建生成器的实例,当然也可以进一步操作生成器的属性。利用构建器可以很容易修改其默认实现,而且也能定义允许的字符。

public String generateRandomSpecialCharacters(int length) {
    RandomStringGenerator pwdGenerator = new RandomStringGenerator.Builder().withinRange(33, 45)
        .build();
    return pwdGenerator.generate(length);
}

使用RandomStringGenerator的一个限制是缺少在每个集合中设定字符数的能力(Passay的类似功能)。但可以合并多个结果集变相实现。


    public String generateRandomSpecialCharacters(int length) {
        RandomStringGenerator pwdGenerator = new RandomStringGenerator.Builder().withinRange(33, 45)
                .build();
        return pwdGenerator.generate(length);
    }

    public String generateRandomNumbers(int length) {
        RandomStringGenerator pwdGenerator = new RandomStringGenerator.Builder().withinRange(48, 57)
                .build();
        return pwdGenerator.generate(length);
    }

    public String generateRandomAlphabet(int length, boolean lowerCase) {
        int low;
        int hi;
        if (lowerCase) {
            low = 97;
            hi = 122;
        } else {
            low = 65;
            hi = 90;
        }
        RandomStringGenerator pwdGenerator = new RandomStringGenerator.Builder().withinRange(low, hi)
                .build();
        return pwdGenerator.generate(length);
    }

    public String generateRandomCharacters(int length) {
        RandomStringGenerator pwdGenerator = new RandomStringGenerator.Builder().withinRange(48, 57)
                .build();
        return pwdGenerator.generate(length);
    }

public String generateCommonTextPassword() {
    String pwString = generateRandomSpecialCharacters(2).concat(generateRandomNumbers(2))
      .concat(generateRandomAlphabet(2, true))
      .concat(generateRandomAlphabet(2, false))
      .concat(generateRandomCharacters(2));
    List<Character> pwChars = pwString.chars()
      .mapToObj(data -> (char) data)
      .collect(Collectors.toList());
    Collections.shuffle(pwChars);
    String password = pwChars.stream()
      .collect(StringBuilder::new, StringBuilder::append, StringBuilder::append)
      .toString();
    return password;
}

写个单元测试验证生成的密码:

@Test
public void whenPasswordGeneratedUsingCommonsText_thenSuccessful() {
    RandomPasswordGenerator passGen = new RandomPasswordGenerator();
    String password = passGen.generateCommonTextPassword();
    int lowerCaseCount = 0;
    for (char c : password.toCharArray()) {
        if (c >= 97 || c <= 122) {
            lowerCaseCount++;
        }
    }
    assertTrue("Password validation failed in commons-text ", lowerCaseCount >= 2);
}

RandomStringGenerator默认使用ThreadLocalRandom来实现随机性。需要指出的是,这并不能确保加密安全性。
但我们可以使用usingRandom(TextRandomProvider)设置随机性来源。例如,我们可以使用SecureTextRandomProvider进行加密安全:

public String generateRandomSpecialCharacters(int length) {
    SecureTextRandomProvider stp = new SecureTextRandomProvider();
    RandomStringGenerator pwdGenerator = new RandomStringGenerator.Builder()
      .withinRange(33, 45)
      .usingRandom(stp)
      .build();
    return pwdGenerator.generate(length);
}

3. 使用RandomStringUtils

另外也可以利用the Apache Commons Lang库的RandomStringUtils 类。它提供了几个static方法可以实现我们的需求。下面通过示例看如何提供字符范围:

public String generateCommonLangPassword() {
    String upperCaseLetters = RandomStringUtils.random(2, 65, 90, true, true);
    String lowerCaseLetters = RandomStringUtils.random(2, 97, 122, true, true);
    String numbers = RandomStringUtils.randomNumeric(2);
    String specialChar = RandomStringUtils.random(2, 33, 47, false, false);
    String totalChars = RandomStringUtils.randomAlphanumeric(2);
    String combinedChars = upperCaseLetters.concat(lowerCaseLetters)
      .concat(numbers)
      .concat(specialChar)
      .concat(totalChars);
    List<Character> pwdChars = combinedChars.chars()
      .mapToObj(c -> (char) c)
      .collect(Collectors.toList());
    Collections.shuffle(pwdChars);
    String password = pwdChars.stream()
      .collect(StringBuilder::new, StringBuilder::append, StringBuilder::append)
      .toString();
    return password;
}

可以查看random方法的定义:

    public static String random(int count, int start, int end, boolean letters, boolean numbers) {
        return random(count, start, end, letters, numbers, (char[])null, RANDOM);
    }

单元测试:

@Test
public void whenPasswordGeneratedUsingCommonsLang3_thenSuccessful() {
    RandomPasswordGenerator passGen = new RandomPasswordGenerator();
    String password = passGen.generateCommonsLang3Password();
    int numCount = 0;
    for (char c : password.toCharArray()) {
        if (c >= 48 || c <= 57) {
            numCount++;
        }
    }
    assertTrue("Password validation failed in commons-lang3", numCount >= 2);
}

这里,RandomStringUtils默认使用Random作为随机因子。但它也提供了一个方法可以让我们指定随机因子:

String lowerCaseLetters = RandomStringUtils.random(2, 97, 122, true, true, null, new SecureRandom());

现在可以使用SecureRandom的实例来确保加密安全性。但此功能不能扩展到库中的其他方法。顺便提一句,一般建议仅在简单用例中使用RandomStringUtils。

4. 自定义方法

我们也能利用SecureRandom 类创建自定义工具类实现。首先我们生成两个长度的特殊字符:

public Stream<Character> getRandomSpecialChars(int count) {
    Random random = new SecureRandom();
    IntStream specialChars = random.ints(count, 33, 45);
    return specialChars.mapToObj(data -> (char) data);
}

public Stream<Character> getRandomNumbers(int count) {
    IntStream numbers = random.ints(count, 48, 57);
    return numbers.mapToObj(data -> (char) data);
}

public Stream<Character> getRandomAlphabets(int count, boolean upperCase) {
    IntStream characters = null;
    if (upperCase) {
        characters = random.ints(count, 65, 90);
    } else {
        characters = random.ints(count, 97, 122);
    }
    return characters.mapToObj(data -> (char) data);
}

需要提醒的是33和45表示Unicode 字符范围。限制针对上述需求生成多个流,接着合并结果生成密码:


public String generateSecureRandomPassword() {
    Stream<Character> pwdStream = Stream.concat(getRandomNumbers(2), 
      Stream.concat(getRandomSpecialChars(2), 
      Stream.concat(getRandomAlphabets(2, true), getRandomAlphabets(4, false))));
    List<Character> charList = pwdStream.collect(Collectors.toList());
    Collections.shuffle(charList);
    String password = charList.stream()
        .collect(StringBuilder::new, StringBuilder::append, StringBuilder::append)
        .toString();
    return password;
}

同样可以写单元测试进行验证,这里略过。

5. 总结

本文他们介绍了多种方法生成符合一定策略的密码。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值