【c#】Blazor を使用してパスワードの生成をしてみる【SPA】


実際のページはこちら
パスワードの生成ツールを作成してみました。
文字数や生成個数を設定でき、数字、大文字、記号の設定が可能です。
記号は自分で使用する文字の記載が可能で、似通った文字は除外し、除外する文字の設定も可能です。
クライアント側で使用する文字を入力するのは、想定外の文字が入力されて不具合が起きるなど、セキュリティ面も気になりますが、Blazor WebAssenbly を使用し、クライアント側で処理が完結するため、大丈夫なんじゃないかと思っています。

Index.razor の html 部分

@page "/"
@inject IJSRuntime JSRuntime
@using System.Text
@using System.Text.RegularExpressions

<PageTitle>パスワードの生成</PageTitle>

<h1>パスワードの生成</h1>
<hr />
<div class="row">
  <div class="col-auto">
    <div class="input-group mb-3">
      <div class="input-group-text">文字数</div>
        <input type="number" min="3" max="20" class="form-control" @bind="length">
    </div>
    <div class="input-group">
      <div class="input-group-text">生成数</div>
        <input type="number" min="3" max="100" class="form-control" @bind="quantity">
    </div>
  </div>
  <div class="col-auto">
    <div class="form-check">
      <label class="form-check-label">
      <input type="checkbox" class="form-check-input" @bind="useDigits">
        数字を使用する
      </label>
    </div>
    <div class="form-check">
      <label class="form-check-label">
      <input type="checkbox" class="form-check-input" @bind="useUppercaseLetters">
        大文字を使用する
      </label>
    </div>
    <div class="form-check">
      <label class="form-check-label">
      <input type="checkbox" class="form-check-input" @bind="useSymbols">
        記号 <input type="text" @bind="symbols" class="form-control form-control-sm w-auto d-inline-block" /> を使用する
      </label>
    </div>
    <div class="form-check">
      <label class="form-check-label">
      <input type="checkbox" class="form-check-input" @bind="excludeSimilarCharacters">
        似通った文字 <input type="text" @bind="excludeCharacters" class="form-control form-control-sm w-auto d-inline-block" /> を除外する
      </label>
    </div>
    <div class="form-check">
      <label class="form-check-label">
      <input type="checkbox" class="form-check-input" @bind="displayVertical">
        結果を縦に表示
      </label>
    </div>
  </div>
</div>
<button class="btn btn-primary mt-4" @onclick="GetPasswords">作成</button>
<button class="btn btn-primary mt-4" @onclick="CopyText">コピー</button>
<hr />
<div style="font-family:monospace;" id="copy">
@((MarkupString)result)
</div>

Index.razor のコード部分

@code {
    private bool useDigits = true;
    private bool useUppercaseLetters = true;
    private bool useSymbols = false;
    private string symbols = "!@#$%^&*-+={[}]?";
    private bool excludeSimilarCharacters = true;
    private string excludeCharacters = "louv10DIOQUV";
    private int length = 10;
    private int  quantity= 10;
    private bool displayVertical = true;
    private string result = "ここに結果が表示されます。";

    private async Task CopyText()
    {
        await JSRuntime.InvokeVoidAsync("copyText");
    }

    protected override async Task OnInitializedAsync()
    {
        GetPasswords();
    }

    private void GetPasswords()
    {
        if (displayVertical == false)
        {
            result = "<table><tr><td class='pe-3'>";
        }
        else
        {
            result = "";
        }
        var passwordList = new List<string>();
        for (var i = 0; i < quantity; i++)
        {
            passwordList.Add(GeneratePassword());
        }
        result += String.Join(displayVertical ? "<br>" : "<td class='pe-3'>", passwordList);
        if (displayVertical == false)
        {
            result += "</tr></table>";
        }
    }
    private string GeneratePassword()
    {
        string characters = "abcdefghijklmnopqrstuvwxyz";
        if (useDigits) characters += "1234567890";
        if (useUppercaseLetters) characters += "ABCDEFGHIJKLMNOPQRLSTUVWXYZ";
        if (useSymbols) characters += symbols.Trim();

        if (excludeSimilarCharacters)
        {
            foreach(char c in excludeCharacters.Trim().ToCharArray())
            {
                characters = characters.Replace(c.ToString(), "");
            }
        }

        StringBuilder passwordBuilder = new StringBuilder();
        Random random = new Random();
        while (true)
        {
            for (int i = 0; i < length; i++)
            {
                int position = random.Next(characters.Length);
                char c = characters[position];
                passwordBuilder.Append(c);
            }
            if (
                    Regex.IsMatch(passwordBuilder.ToString(), "[a-z]")
                    && (useDigits == false || Regex.IsMatch(passwordBuilder.ToString(), "[0-9]"))
                    && (useUppercaseLetters == false || Regex.IsMatch(passwordBuilder.ToString(), "[A-Z]"))
                    && (useSymbols == false || Regex.IsMatch(passwordBuilder.ToString(), "[!@#$%^&*-+={[}]?]"))
                )
            {
                return passwordBuilder.ToString();
            }
            else
            {
                passwordBuilder.Clear();
            }
        }
     }
}

index.html ページの変更

javascript は blazor では.razor ページ上には直接書けないようなので、wwwroot の index.html の body の閉じタグの前に以下を追加する。
生成したテキストをコピーする javascript

<script>
    function copyText() {
        var target = document.getElementById("copy");
        document.getSelection().selectAllChildren(target);
        document.execCommand("copy");
        document.getSelection().empty();
        alert("コピーしました。");
    }
</script>

感想

今回はコピー機能を使用するため部分的に javascript を使用しましたが、それ以外ではフロントエンド側でも c#で完結できるのはすっきりしていて良いと思いました。


Programming Blog