Java密码生成器

26

我正在尝试创建一个Java程序,用于生成密码,可以是全小写、小写和大写、小写和大写的数字、小写和大写的数字和标点符号中的任意一种,并且该程序还必须创建用户选择的密码,并根据用户选择生成密码长度。我已经生成了供用户选择的密码选项并提示他选择一个。现在我卡在如何创建上述密码类型。有人建议我使用ASCII值,然后将它们转换为文本。我知道如何将它们转换为文本,但它会显示数字、字母和标点符号。是否有任何方法可以仅为小写字母生成ASCII值?另外,如何根据用户给出的长度生成密码?


你目前尝试了什么?给我们展示一下你的代码。顺便说一句,看一下https://code.google.com/p/vt-middleware/wiki/vtpassword - user1907906
18个回答

61

我使用这个不可变类。
它使用构建器模式
它不支持扩展。

public final class PasswordGenerator {

    private static final String LOWER = "abcdefghijklmnopqrstuvwxyz";
    private static final String UPPER = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    private static final String DIGITS = "0123456789";
    private static final String PUNCTUATION = "!@#$%&*()_+-=[]|,./?><";
    private boolean useLower;
    private boolean useUpper;
    private boolean useDigits;
    private boolean usePunctuation;

    private PasswordGenerator() {
        throw new UnsupportedOperationException("Empty constructor is not supported.");
    }

    private PasswordGenerator(PasswordGeneratorBuilder builder) {
        this.useLower = builder.useLower;
        this.useUpper = builder.useUpper;
        this.useDigits = builder.useDigits;
        this.usePunctuation = builder.usePunctuation;
    }

    public static class PasswordGeneratorBuilder {

        private boolean useLower;
        private boolean useUpper;
        private boolean useDigits;
        private boolean usePunctuation;

        public PasswordGeneratorBuilder() {
            this.useLower = false;
            this.useUpper = false;
            this.useDigits = false;
            this.usePunctuation = false;
        }

        /**
         * Set true in case you would like to include lower characters
         * (abc...xyz). Default false.
         *
         * @param useLower true in case you would like to include lower
         * characters (abc...xyz). Default false.
         * @return the builder for chaining.
         */
        public PasswordGeneratorBuilder useLower(boolean useLower) {
            this.useLower = useLower;
            return this;
        }

        /**
         * Set true in case you would like to include upper characters
         * (ABC...XYZ). Default false.
         *
         * @param useUpper true in case you would like to include upper
         * characters (ABC...XYZ). Default false.
         * @return the builder for chaining.
         */
        public PasswordGeneratorBuilder useUpper(boolean useUpper) {
            this.useUpper = useUpper;
            return this;
        }

        /**
         * Set true in case you would like to include digit characters (123..).
         * Default false.
         *
         * @param useDigits true in case you would like to include digit
         * characters (123..). Default false.
         * @return the builder for chaining.
         */
        public PasswordGeneratorBuilder useDigits(boolean useDigits) {
            this.useDigits = useDigits;
            return this;
        }

        /**
         * Set true in case you would like to include punctuation characters
         * (!@#..). Default false.
         *
         * @param usePunctuation true in case you would like to include
         * punctuation characters (!@#..). Default false.
         * @return the builder for chaining.
         */
        public PasswordGeneratorBuilder usePunctuation(boolean usePunctuation) {
            this.usePunctuation = usePunctuation;
            return this;
        }

        /**
         * Get an object to use.
         *
         * @return the {@link gr.idrymavmela.business.lib.PasswordGenerator}
         * object.
         */
        public PasswordGenerator build() {
            return new PasswordGenerator(this);
        }
    }

    /**
     * This method will generate a password depending the use* properties you
     * define. It will use the categories with a probability. It is not sure
     * that all of the defined categories will be used.
     *
     * @param length the length of the password you would like to generate.
     * @return a password that uses the categories you define when constructing
     * the object with a probability.
     */
    public String generate(int length) {
        // Argument Validation.
        if (length <= 0) {
            return "";
        }

        // Variables.
        StringBuilder password = new StringBuilder(length);
        Random random = new Random(System.nanoTime());

        // Collect the categories to use.
        List<String> charCategories = new ArrayList<>(4);
        if (useLower) {
            charCategories.add(LOWER);
        }
        if (useUpper) {
            charCategories.add(UPPER);
        }
        if (useDigits) {
            charCategories.add(DIGITS);
        }
        if (usePunctuation) {
            charCategories.add(PUNCTUATION);
        }

        // Build the password.
        for (int i = 0; i < length; i++) {
            String charCategory = charCategories.get(random.nextInt(charCategories.size()));
            int position = random.nextInt(charCategory.length());
            password.append(charCategory.charAt(position));
        }
        return new String(password);
    }
}

这是一个用法示例,

PasswordGenerator passwordGenerator = new PasswordGenerator.PasswordGeneratorBuilder()
        .useDigits(true)
        .useLower(true)
        .useUpper(true)
        .build();
String password = passwordGenerator.generate(8); // output ex.: lrU12fmM 75iwI90o

1
这真的很有用。 - kinsley kajiva
1
它真的非常快。 - Fencer
8
这并不保证生成的密码完全符合“builder”的设置。例如:即使您设置了.useUpper(true),密码可能不包含任何大写字符。 - frzsombor

20
你可以使用org.apache.commons.lang.RandomStringUtils来生成随机的文本/密码。请参考这个链接获取示例。

14

万一有人需要,这是一个基于ASCII范围的标准Java 8类的单行随机密码生成器:

String password = new Random().ints(10, 33, 122).collect(StringBuilder::new,
        StringBuilder::appendCodePoint, StringBuilder::append)
        .toString();
或者
String password = new Random().ints(10, 33, 122).mapToObj(i -> String.valueOf((char)i)).collect(Collectors.joining());

这里密码的长度为10。当然,您也可以在某个范围内随机设置它。 并且字符来自ASCII范围33-122,这些都是特殊符号、数字、大写和小写字母。

如果您只需要小写字母,只需设置范围为:97-122


3
ints() 的最后一个参数是左闭右开区间,因此这不会包括代码点 122(_z_),应该改为 123。如果您使用特殊字符,最好将其提高到 127,其中包括 {, |, }, 和 _~_。 - Raniz
4
我会把new Random()换成new SecureRandom(),把.ints(10, 33, 122)改为更易懂的.ints(10, '!', '{') - Andriy Kryvtsun

10
import java.security.SecureRandom;
import java.util.Random;

public class PasswordHelper {        

    public static String generatePassword (int length) {

    //minimum length of 6
    if (length < 4) {
        length = 6;
    }

    final char[] lowercase = "abcdefghijklmnopqrstuvwxyz".toCharArray();
    final char[] uppercase = "ABCDEFGJKLMNPRSTUVWXYZ".toCharArray();
    final char[] numbers = "0123456789".toCharArray();
    final char[] symbols = "^$?!@#%&".toCharArray();
    final char[] allAllowed = "abcdefghijklmnopqrstuvwxyzABCDEFGJKLMNPRSTUVWXYZ0123456789^$?!@#%&".toCharArray();

    //Use cryptographically secure random number generator
    Random random = new SecureRandom();

    StringBuilder password = new StringBuilder(); 

    for (int i = 0; i < length-4; i++) {
        password.append(allAllowed[random.nextInt(allAllowed.length)]);
    }

    //Ensure password policy is met by inserting required random chars in random positions
    password.insert(random.nextInt(password.length()), lowercase[random.nextInt(lowercase.length)]);
    password.insert(random.nextInt(password.length()), uppercase[random.nextInt(uppercase.length)]);
    password.insert(random.nextInt(password.length()), numbers[random.nextInt(numbers.length)]);
    password.insert(random.nextInt(password.length()), symbols[random.nextInt(symbols.length)]);
    }

    return password.toString();

    }

}

这应该是被接受的答案;它是唯一一个保证密码包含所需的大写字母/小写字母/数字/符号组合的答案。 - Roger

3

另一个随机字符串生成器

George Siggouroglou创建了一个非常好的答案。我用更加灵活的方式修改和完善了他的代码。

特点

  1. 使用SecureRandom代替Random。你可以将其改为Random
  2. 增加最小字符数限制。首先,算法会从所有可用字符中随机选择最少所需字符。然后再用其它可用字符补全到指定长度。之后,所有选定的字符都会被随机交换位置。
  3. 你可以使用 custom 方法来自定义词汇表。

示例用法

public class Main {
    public static void main(String[] args) {
        String password = new PasswordGenerator.Builder()
                .digits(4)
                .lower(1)
                .upper(2)
                .punctuation()
                .generate(10);

        System.out.println(password);

        // with custom
        password = new PasswordGenerator.Builder()
                .custom("1579", 1)
                .custom("Stackoverflow", 3)
                .lower()
                .generate(6);
        System.out.println(password);

    }
}

源代码

import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

public class PasswordGenerator {
    private final List<Rule> rules;

    private PasswordGenerator() {
        throw new UnsupportedOperationException("Empty constructor is not supported.");
    }

    private PasswordGenerator(Builder builder) {
        this.rules = builder.rules;
    }

    public String generate(int length) {
        if (length <= 0) {
            return "";
        }

        // shuffle rules
        List<Rule> shuffledRules = new ArrayList<>(rules);
        Collections.shuffle(shuffledRules);

        // random instance, you can use `Random random = new Random();`
        Random random = new SecureRandom();

        // 1. STAGE - SELECT MINIMUM CHARACTERS FROM RULES THAT HAVE MINIMUM COUNT.
        List<Character> selectedChars = new ArrayList<>(selectCharactersByMinimumCount(random, shuffledRules));

        // 2. STAGE - SELECT MISSING LENGTH FROM ALL AVAILABLE CHARACTERS
        int missingLength = length - selectedChars.size();
        if (missingLength > 0) {
            selectedChars.addAll(selectCharactersByMissingLength(random, shuffledRules, missingLength));
        }

        // 3. STAGE - SHUFFLE SELECTED CHARS
        Collections.shuffle(selectedChars);

        // 4. STAGE - RETURN RESULT
        return selectedChars.stream().map(String::valueOf).collect(Collectors.joining());
    }

    /**
     * Select random characters from filter rules that they are defined minimum count value.
     *
     * @param random Random instance
     * @param rules  Rules
     * @return Randomly chosen characters
     */
    private List<Character> selectCharactersByMinimumCount(Random random, List<Rule> rules) {
        return rules.stream()
                .filter(rule -> rule.minimumCount > 0)
                .flatMap(rule ->
                        IntStream.range(0, rule.minimumCount)
                                .mapToObj(i -> rule.text.charAt(random.nextInt(rule.text.length()))))
                .collect(Collectors.toList());
    }

    /**
     * Select random characters from all filter rules up to complete required characters count.
     *
     * @param random Random instance
     * @param rules  Rules
     * @return Randomly chosen characters
     */
    private List<Character> selectCharactersByMissingLength(Random random, List<Rule> rules, int missingLength) {
        List<Character> availableList = rules.stream()
                .flatMap(rule -> rule.text.chars().mapToObj(c -> (char) c))
                .collect(Collectors.toList());
        // shuffle available list
        Collections.shuffle(availableList);

        return IntStream.range(0, missingLength)
                .mapToObj(i -> availableList.get(random.nextInt(availableList.size()))).collect(Collectors.toList());
    }

    public static class Builder {
        private static final String LOWER = "abcdefghijklmnopqrstuvwxyz";
        private static final String UPPER = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
        private static final String DIGITS = "0123456789";
        private static final String PUNCTUATION = "!@#$%&*+-";

        private final List<Rule> rules = new ArrayList<>();

        public Builder digits() {
            return custom(DIGITS, 0);
        }

        public Builder digits(int minimumCount) {
            return custom(DIGITS, minimumCount);
        }

        public Builder lower() {
            return lower(0);
        }

        public Builder lower(int minimumCount) {
            return custom(LOWER, minimumCount);
        }

        public Builder upper() {
            return upper(0);
        }

        public Builder upper(int minimumCount) {
            return custom(UPPER, minimumCount);
        }

        public Builder punctuation() {
            return punctuation(0);
        }

        public Builder punctuation(int minimumCount) {
            return custom(PUNCTUATION, minimumCount);
        }

        public Builder custom(String text) {
            return custom(text, 0);
        }

        public Builder custom(String text, int minimumCount) {
            rules.add(new Rule(text, minimumCount));
            return this;
        }

        public PasswordGenerator build() {
            return new PasswordGenerator(this);
        }

        public String generate(int length) {
            return build().generate(length);
        }
    }

    private static class Rule {
        private final String text;
        private final int minimumCount;

        public Rule(String text, int minimumCount) {
            this.text = text;
            this.minimumCount = minimumCount;
        }
    }
}

3

这是 George Siggouroglou 的答案(点击查看)的简略版本,虽然不够灵活但更为简短。

生成器类

public final class DefaultPasswordGenerator {
    private static final String[] charCategories = new String[] {
            "abcdefghijklmnopqrstuvwxyz",
            "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
            "0123456789"
    };

    public static String generate(int length) {
        StringBuilder password = new StringBuilder(length);
        Random random = new Random(System.nanoTime());

        for (int i = 0; i < length; i++) {
            String charCategory = charCategories[random.nextInt(charCategories.length)];
            int position = random.nextInt(charCategory.length());
            password.append(charCategory.charAt(position));
        }

        return new String(password);
    }
}

使用方法

String passWord = PasswordGenerator.generate(10);

3
您可以这样做:
String lower = "abc...xyz";
String digits = "0123456789";
String punct = "!#$&...";
String  ...                      // further characer classes

请注意...部分需要自己填写。

从用户选择的选项中,通过连接相应的字符类别创建一个可供选择的字符字符串。

最后你要运行一个循环n次,其中n是所需字符的数量。每一轮中,你从所创建的字符串中随机选择一个字符并将其添加到结果中:

StringBuilder sb = new StringBuilder();
int n = ....; // how many characters in password
String set = ....; // characters to choose from

for (i= 0; i < n; i++) {
    int k = ....;   // random number between 0 and set.length()-1 inklusive
    sb.append(set.charAt(k));
}
String result = sb.toString();

2

Apache commons text有一个非常好的随机字符串生成的替代方案。使用Builder来构建生成器,然后可以轻松地使用该生成器来生成所需的密码。

 // Generates a 20 code point string, using only the letters a-z
 RandomStringGenerator generator = new RandomStringGenerator.Builder()
     .withinRange('a', 'z').build();
 String randomLetters = generator.generate(20);

请查看。

https://commons.apache.org/proper/commons-text/javadocs/api-release/org/apache/commons/text/RandomStringGenerator.html


2
你可以使用这个简单的算法生成你想要的密码。
public static final String upperChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    public static final String lowerChars = "abcdefghijklmnopqrstuvwxyz";
    public static final String numbers = "1234567890";
    public static final String specialChars = "!@#$%^&*()_+{}";

    public static String generatePassword(
            int passwordSize,
            boolean useUpper,
            boolean useLower,
            boolean useNumbers,
            boolean useSpecial
    ) {
        char[] password = new char[passwordSize];
        String charSet = null;
        Random random = new Random();

        if (useUpper) charSet += upperChars;
        if (useLower) charSet += lowerChars;
        if (useNumbers) charSet += numbers;
        if (useSpecial) charSet += specialChars;

        for (int i = 0; i < passwordSize; i++) {
            password[i] = charSet.toCharArray()[random.nextInt(charSet.length() - 1)];
        }
        return String.valueOf(password);
    }

使用方法

generatePassword(22, true, true, true, true);

这将返回一个字符串,因此可以直接使用而不需要转换。


1
你可以随机选择数字、字母和标点符号,具有一个维度。Ansii 数字从 30 到 39,小写字母从 61-7A,等等。使用 ansii 表格

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接