Windowsで動くパスワード管理ツールをJavaFXで作る

Windows上のGUIで動作するパスワード管理ツールを作成しました。

まずは必要最低限の機能を持たせました。

  1. パスワード名称の一覧表示
  2. 絞り込み機能
  3. クリップボードへコピー

経緯について

コロナ過に入ってからずっとリモートワークが続き、Slack等で画面を共有しながら作業することがよくあります。

その際にパスワードをメモからコピーするときにさらけ出してしまうのを防ぎたかったからです。

2画面以上のマルチディスプレイであれば共有していないほうのディスプレイでコピーすれば済むんですが、1画面のためそうはいかず...

またWindowsのアプリケーションでも探してみましたが、有償であったり会員登録が必要であったりと、これといったものが無かったのでJavaFxの勉強がてらに作ってみました。

環境

  • Windows10
  • vscode
  • JDK11

プロジェクト作成

  1. vscodeを開き「Ctrl+Shift+P」で「Java:Create Java Project...」を選択

  2. Project type に No build tools を選択

  3. Project を作成する親フォルダを選択
    ここでは C:¥workspace を選択

  4. Project名を入力

  5. 以下のような構成でプロジェクトが作成される

  6. 実行は右上の > ボタン

    Terminalに「Hello, World!」が出力されればひとまずOK

ライブラリ

JavaFX SDKダウンロード

JavaでGUIを実装するためのフレームワークです。

Windows版のSDKをダウンロードします。

https://gluonhq.com/products/javafx/

ダウンロード後解凍し、任意のディレクトリに配置します。

ここでは C:\workspace\password-manager-test\lib\javafx へ配置しました。

JavaFX SDKインポート

  1. vscodeのエクスプローラーから、Java Projectを開く
  2. Referenced Libraries → +ボタン
  3. 配置したディレクトリのlibディレクトリまで移動
  4. *.jarファイルをすべて選択し、Select Jar Librariesでインポート

jackson-databind JAR

https://jar-download.com/artifacts/com.fasterxml.jackson.core/jackson-databind

パスワードの保存ファイルにjsonを採用し、ObjectMapperを使って読み込みたいため、jackson-databind JARをダウンロードし、JacvFXと同様の手順でインポートします。

実行とデバッグの設定

左側メニューの実行とデバッグから「launch.jsonファイルを作成」をクリックします。

以下のようなJSONファイルが作成されるのでconfigurationsの最終行に varArgs を設定します。

{
    // IntelliSense を使用して利用可能な属性を学べます。
    // 既存の属性の説明をホバーして表示します。
    // 詳細情報は次を確認してください: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
        {
            "type": "java",
            "name": "Launch App",
            "request": "launch",
            "mainClass": "App",
            "projectName": "password-manager-test_5bebe0f2",
            // ↓追加行
            "vmArgs": "--module-path \"C:\\workspace\\password-manager-test\\lib\\javafx\\lib\" --add-modules javafx.controls,javafx.fxml"
        }
    ]
}

サンプル動作確認

App.java

以下コードをApp.javaに貼り付けて実行し画面が立ち上がればひとまずOKです。

javafxを使って開発する環境が整いました。

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

public class App extends Application {
    public static void main(String[] args) throws Exception {
        launch();
    }

    @Override
    public void start(Stage stage) throws Exception {
        Label l = new Label("Hello, World!");
        Scene scene = new Scene(new StackPane(l), 640, 480);
        stage.setScene(scene);
        stage.show();
    }
}

実装

アプリケーションの実装

App.java

以下のように修正します。

import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.TextField;
import javafx.scene.input.Clipboard;
import javafx.scene.input.ClipboardContent;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class App extends Application {

    List<Button> buttonList;
    
    Clipboard clipboard;

    public static void main(String[] args) throws Exception {
        launch();
    }

    @Override
    public void start(Stage stage) throws Exception {
        stage.setTitle("Password Manager");

        clipboard = Clipboard.getSystemClipboard();

        // Jsonデータの読み込み
        String json = "";
        for (String buff : Files.readAllLines(Paths.get("C:\\workspace\\password-manager-test\\data.json"))) {
            json += buff;
        }

        // JsonデータをMapへ変換するためのオブジェクト
        ObjectMapper mapper = new ObjectMapper();
        Map<String, Object> data = mapper.readValue(json, new TypeReference<Map<String,Object>>(){});

        // 検索テキストボックスの設定
        // Key入力時にリアルタイムで検索する
        TextField search = new TextField();
        search.setOnKeyTyped(new EventHandler<KeyEvent>(){
            @Override
            public void handle(KeyEvent e) {
                String keyword = search.getText().trim();
                for (Button button : buttonList) {
                    if (button.getText().toLowerCase().contains(keyword.toLowerCase())) {
                        button.setVisible(true);
                        button.setManaged(true);
                    } else {
                        button.setVisible(false);
                        button.setManaged(false);
                    }
                }
            }
        });

        // 各種ボタンの設定
        // クリック時にパスワードをクリップボードにコピーする
        buttonList = new ArrayList<>();
        int i = 0;
        for (String key : data.keySet()) {
            Button button = new Button(key);
            button.setId(key);
            button.setAlignment(Pos.CENTER_LEFT);
            button.setUserData(data.get(key.toString()));
            button.setOnAction(new EventHandler<ActionEvent>(){
                @Override
                public void handle(ActionEvent e) {
                    Button target = (Button) e.getSource();
                    ClipboardContent content = new ClipboardContent();;
                    content.putString(target.getUserData().toString());
                    clipboard.setContent(content);
                }
            });

            buttonList.add(button);
            buttonList.get(i).setPrefWidth(480);
            i++;
        }

        // コントロールをパネルにセット
        VBox vBox = new VBox();
        vBox.setAlignment(Pos.TOP_CENTER);
        vBox.setPadding(new Insets(10, 10, 10, 10));
        vBox.getChildren().addAll(search);
        vBox.getChildren().addAll(buttonList);
 
        Scene scene = new Scene(new ScrollPane(vBox), -1, 600);
        stage.setScene(scene);
        stage.show();
    }
}

データファイル

data.json

パスワードを保存するJSONファイルです。

単純なMapオブジェクトでKey(名称)とValue(パスワード)の組み合わせで構成しています。

{
    "mailaddress@test.com":"test1234"
    ,"Dev-Server password":"dev1234"
    ,"CMS password 1":"cmspass1"
    ,"CMS password 2":"cmspass2"
}

動作確認

アプリケーションを実行するとJSONファイルが読み込まれ登録したパスワードの名称が一覧表示されます。

任意のボタンをクリックするとクリップボードへパスワード(Value)がコピーされるので、冒頭の目的はひとまず達成されました。



このままだとvscodeを閉じたり、workspaceを切り替えるとアプリケーションも終了してしまいます。

何かと不便なので、とりあえず次回これをMavenプロジェクトで作り直したいと思います。

コメント

このブログの人気の投稿

docker-compose up で proxyconnect tcp: dial tcp: lookup proxy.example.com: no such host

docker-compose で起動したweb、MySQLに接続できない事象

【PHP】PHP_CodeSnifferを使う(コーディングルールのカスタマイズ)