본문 바로가기

Slack 채널 정리

[java]random password 생성

랜덤 패스워드 생성하는 로직은 평이하다 보니 대부분 직접 구현하겠지만 오늘도 게으름에 주워다 쓸만한 샘플 좀 찾아봤습니다. https://stackoverflow.com/questions/19743124/java-password-generator 여기 답변에 있는 샘플 소스 깔끔해 보여서 여기다 조금 살 보태봤습니다.

1. exceptPunctuationChars() 추가 : PUNCTUATION 중 동적으로 일부 문자를 제외할 수 있도록 해주었습니다. 
뭐 이런 식으로 쓸 수 있겠죠.

String exceptChars = "-|\\[|\\]|\\?";PasswordGenerator passwordGenerator = new PasswordGenerator.PasswordGeneratorBuilder()
       .useDigits(true)
       .useLower(true)
       .useUpper(true)
       .exceptPunctuationChars(exceptChars)
       .build();

2.  generate(int length) 일부 수정.
원 샘플에서는 가령 .useLower(true) 한다고 하더라도 생성되는 패스워드에 소문자 포함 여부도 랜덤하게 됩니다.
그런데, 실제 우리가 사용하는 환경에서는 보통 '대문자, 소문자 등이 최소 1개 이상 포함되어야 한다' 식의 정책이 쓰이겠죠.
그래서 사용하기로 설정한 문자셋은 필수 1글자 이상 포함되도록 아래 처럼 살짝 고쳤습니다.

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

for (int i = 0; i < charCategories.size(); i++) {
   String charCategory = charCategories.get(i);
   int position = random.nextInt(charCategory.length());
   password.insert(random.nextInt(length - charCategories.size()), charCategory.charAt(position));
}
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

public final class PasswordGenerator {
    private static final String LOWER = "abcdefghijklmnopqrstuvwxyz";
    private static final String UPPER = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    private static final String DIGITS = "0123456789";
    private static 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;
        }

        /**
         * Set PUNCTUATION except for user defined characters.
         *
         * @param exceptChars user defined characters( ex : "-|\\[|\\]|\\?" )
         * @return the builder for chaining.
         */
        public PasswordGeneratorBuilder exceptPunctuationChars(String exceptChars) {
            if(exceptChars != null) {
                PUNCTUATION = PUNCTUATION.replaceAll(exceptChars, "");
                this.usePunctuation = true;
            }
            return this;
        }

        /**
         * Get an object to use.
         *
         * @return 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<String>(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 - charCategories.size(); i++) {
            String charCategory = charCategories.get(random.nextInt(charCategories.size()));
            int position = random.nextInt(charCategory.length());
            password.append(charCategory.charAt(position));
        }

        for (int i = 0; i < charCategories.size(); i++) {
            String charCategory = charCategories.get(i);
            int position = random.nextInt(charCategory.length());
            password.insert(random.nextInt(length - charCategories.size()), charCategory.charAt(position));
        }
        return new String(password);
    }
}