【Java】条件に沿って記号英数字を含むランダムなパスワードを生成する
1. はじめに
今回はパスワードポリシーに沿って記号英数字を含むランダムなパスワードを生成してみたいと思います。
具体的な条件は以下の通りです。
※こちらの記事を参考にパスワード生成でよく扱われる条件を追加してみました。
パスワードポリシー | コード上での表現 |
---|---|
大文字アルファベットを含むことを許可あるいは不許可とする | uppercaseEnable = "true"/"false" |
大文字アルファベットを必ず含む | uppercaseEnable = "required" |
小文字アルファベットを含むことを許可あるいは不許可とする | lowercaseEnable = "true"/"false" |
小文字アルファベットを必ず含む | lowercaseEnable = "required" |
数字を含むことを許可あるいは不許可とする | numberEnable = "true"/"false" |
数字を必ず含む | numberEnable = "required" |
記号を含むことを許可あるいは不許可とする | symbolEnable = "true"/"false" |
記号を必ず含む | symbolEnable = "required" |
連続した文字を許可あるいは不許可とする | sameChrEnable = "true"/"false" |
具体的には
- 記号英数字全て必須(
required
)のパスワード:aGft@t0+ - 記号を許可(
true
)・英数字を必須(required
)の場合:ab7nIjss
※許可設定の文字種は無くてもよい
といったイメージです。
2.ソースコードと結果
先にソースコードとパスワードの出力例を紹介します。
■ CreatePassword.java
作成したソースコードです。
import java.util.Random;
public class CreatePassword {
public static void main(String[] args) {
String password;
password = createPassword(8,"true","true","true","true", "true");
System.out.println(password);
}
public static String createPassword(int passwordMinLength,
String lowercaseEnable,
String uppercaseEnable,
String numberEnable,
String symbolEnable,
String sameChrEnable) {
StringBuilder passwordBase = new StringBuilder();
StringBuilder lowercaseBase = new StringBuilder("abcdefghijklmnopqrstuvwxyz");
StringBuilder uppercaseBase = new StringBuilder("ABCDEFGHIJKLMNOPQRSTUVWXYZ");
StringBuilder numberBase = new StringBuilder("0123456789");
StringBuilder symbolBase = new StringBuilder("`˜!@#$%^&*()_+-={}[]|:;\"'<>,.?/");
StringBuilder password = new StringBuilder();
if (lowercaseEnable == "true"|| lowercaseEnable == "required") {
passwordBase.append(lowercaseBase);
}
if (uppercaseEnable == "true" || uppercaseEnable == "required") {
passwordBase.append(uppercaseBase);
}
if (numberEnable == "true" || numberEnable == "required") {
passwordBase.append(numberBase);
}
if (symbolEnable == "true" || symbolEnable == "required") {
passwordBase.append(symbolBase);
}
// requiredが指定されている場合、必ず指定された文字種が含まれるようにする
if (lowercaseEnable == "required") {
password.append(lowercaseBase.charAt(new Random().nextInt(lowercaseBase.length())));
}
if (uppercaseEnable == "required") {
password.append(uppercaseBase.charAt(new Random().nextInt(uppercaseBase.length())));
}
if (numberEnable == "required") {
password.append(numberBase.charAt(new Random().nextInt(numberBase.length())));
}
if (symbolEnable == "required") {
password.append(symbolBase.charAt(new Random().nextInt(symbolBase.length())));
}
char prevChar = '\0'; // 1つ前に生成された値を格納する変数
// passwordに文字列が格納されている場合、末尾の文字を格納する
if (password.length() != 0) {
prevChar = password.charAt(password.length() - 1) ;
}
// パスワード生成
Random rand = new Random();
for (int i = password.length(); i < passwordMinLength; i++) {
int num = rand.nextInt(passwordBase.length());
char c = passwordBase.charAt(num);
// 連続文字許可設定が有効でない場合、連続した文字が生成されないようにする
while (sameChrEnable != "true" && c == prevChar) {
num = rand.nextInt(passwordBase.length());
c = passwordBase.charAt(num);
}
password.append(c);
prevChar = c;
}
return password.toString();
}
}
■ 出力結果
今回は全文字種を必ず含むように生成してみました。
gH6<l-!x
3.解説
ソースコードの解説です。
■ パスワード生成で扱う文字の定義
まず、パスワード候補となる文字をStringBuilderクラス
を使い定義します。
StringBuilder passwordBase = new StringBuilder();
StringBuilder lowercaseBase = new StringBuilder("abcdefghijklmnopqrstuvwxyz");
StringBuilder uppercaseBase = new StringBuilder("ABCDEFGHIJKLMNOPQRSTUVWXYZ");
StringBuilder numberBase = new StringBuilder("0123456789");
StringBuilder symbolBase = new StringBuilder("`˜!@#$%^&*()_+-={}[]|:;\"'<>,.?/");
StringBuilder password = new StringBuilder();
■ 文字種の許可/不許可処理
各文字種の許可/不許可設定に基づいてpasswordBase
に使用する文字を挿入していきます。
passwordBase
は生成されるパスワード候補となる文字を格納するための変数です。
if (lowercaseEnable == "true"|| lowercaseEnable == "required") {
passwordBase.append(lowercaseBase);
}
if (uppercaseEnable == "true" || uppercaseEnable == "required") {
passwordBase.append(uppercaseBase);
}
if (numberEnable == "true" || numberEnable == "required") {
passwordBase.append(numberBase);
}
if (symbolEnable == "true" || symbolEnable == "required") {
passwordBase.append(symbolBase);
}
True
またはreauired
の場合
【具体例】小文字アルファベットと数字がpasswordBase
は以下の文字列が格納されます。
passwordBase = "abcdefghijklmnopqrstuvwxyz0123456789"
■ 必ず特定の文字種を含める場合の処理
必ず特定文字種を含める場合(required指定)の処理です。
ここでは、(lowercase/uppercase/number/symbol)Base
に対してランダム関数で文字を抽出し、
生成パスワードとなるpassword
に直接特定文字種を挿入しています。
// requiredが指定されている場合、必ず指定された文字種が含まれるようにする
if (lowercaseEnable == "required") {
password.append(lowercaseBase.charAt(new Random().nextInt(lowercaseBase.length())));
}
if (uppercaseEnable == "required") {
password.append(uppercaseBase.charAt(new Random().nextInt(uppercaseBase.length())));
}
if (numberEnable == "required") {
password.append(numberBase.charAt(new Random().nextInt(numberBase.length())));
}
if (symbolEnable == "required") {
password.append(symbolBase.charAt(new Random().nextInt(symbolBase.length())));
}
reauired
の場合、
【具体例】小文字アルファベットと数字がlowercaseBase = "abcdefghijklmnopqrstuvwxyz"
numberBase = "0123456789"
上記からそれぞれランダムに文字を1文字ずつ抽出しpassword
に格納します。
結果として、password
は以下のようになります。
password = "j6"
■ 連続文字許可/不許可設定のための事前準備
パスワード生成処理内で行われる連続した文字の許可/不許可処理のための事前準備をします。
連続する文字の比較を行うためにあらかじめ生成される文字の1つ前の文字prevChar
を定義します。
また、required
が指定され、既にpassword
に文字が挿入されている場合、最後尾の文字をprevChar
に設定します。
char prevChar = '\0'; // 1つ前に生成された値を格納する変数
// passwordに文字列が格納されている場合、末尾の文字を格納する
if (password.length() != 0) {
prevChar = password.charAt(password.length() - 1) ;
}
【具体例1】必須条件が無い場合
password = ""
となり、prevChar = '\0'
となります。
【具体例2】必須条件が有る場合
password = "j6"
となり、prevChar = "6"
となります。
■ パスワード生成
最後にパスワード生成部分です。
まず、生成候補文字列が格納されているpasswordBase
に対してランダム関数で文字の抽出を行います。
連続文字が許可されていない場合(sameChrEnable != "true"
)は、抽出した文字と1ステップ前の文字と比較し、同じだった場合、再度ランダム関数による文字の抽出を行います。
最終的に選ばれた、文字がpassword
に挿入されていきます。
Random rand = new Random();
for (int i = password.length(); i < passwordMinLength; i++) {
int num = rand.nextInt(passwordBase.length());
char c = passwordBase.charAt(num);
// 連続文字許可設定が有効でない場合、連続した文字が生成されないようにする
while (sameChrEnable != "true" && c == prevChar) {
num = rand.nextInt(passwordBase.length());
c = passwordBase.charAt(num);
}
password.append(c);
prevChar = c;
}
Discussion