収支や資産を一元的に把握し、自分の家計と世帯の家計を分けて管理したい。そのために作ったのがNestraです。実務で使う技術を深めながら、REST APIとKeycloakによる認証・認可を学ぶ個人開発でもあります。

全体の構成

図 1 公開環境向けの構成

利用者はブラウザーでVercel配信のReactを使います。Reactからの認証とAPI通信は、Oracle Cloud上のCaddyを経由してKeycloakとSpring Bootへ届きます。両者のMySQLデータベースは分離しています。

利用者のブラウザーReact SPA / Vercelから配信
HTTPS

Oracle Cloud / Docker Compose

CaddyHTTPSの入口・接続先の振り分け
Caddyから認証
Keycloakログイン・認証情報の管理
MySQLKeycloak用データベース
CaddyからAPI通信
Spring Boot REST API認可・家計の処理
MySQL業務用データベース

MySQLサーバーは共用し、データベースを分離。外部公開はCaddyの80 / 443番ポートのみ。

運用費用を抑えることを選定の前提にしています。図は公開環境向けに用意した構成で、現在の稼働を示すものではありません。

認証はKeycloakへ

図 2 ログインからAPIの利用まで

利用者がReactからログインを始め、Keycloakで認証します。Reactへ戻った後、認可コードとPKCEの検証値でアクセストークンを取得します。ReactがそのトークンをAPIへ送り、Spring SecurityがJWTを検証します。

OpenID Connect / 認可コードフロー + PKCE

  1. 利用者 Reactログインを開始
    Keycloakへ移動
  2. Keycloak利用者を認証
    認可コードを返す
  3. React KeycloakReactへ戻り、PKCEでコードを交換
    アクセストークンを取得
  4. React Spring BootアクセストークンをAPIへ送信
    Spring SecurityがJWTを検証

パスワード管理をAPIから切り離すため、認証はKeycloakに集めました。家計データの利用可否はAPIで判断します。React側でも、トークンをWeb Storageへ保存しない構成にしています。

誰の家計かは、サーバー側で確かめる

図 3 利用できる家計の範囲を決める

サーバーは検証済みJWTの利用者識別子subから利用者を特定します。世帯が指定された場合は最新の所属と役割を確認し、個人または世帯の操作範囲を決めます。クライアントが指定したownerIdを認可の根拠にはしません。

検証済みJWTの sub誰が操作しているか
最新の所属・役割世帯の指定時にデータベースで確認
サーバー側で利用範囲を解決本人の識別と、世帯の所属・権限をもとに判断
個人の家計自分のデータ
または
世帯の家計所属し、権限のあるデータ
画面から渡された ownerId認可の根拠にしない

世帯を切り替える指定は、操作する権限の証明にはなりません。X-Household-Idを受け取るたびに、データベース上の最新の所属と役割を確認します。リクエストで任意のownerIdを渡して、アクセス先を決める仕組みにはしていません。

口座などの参照先も、解決した範囲内で取り直します。世帯の選択だけを確認して、別の家計の口座を更新できてしまう抜け道を防ぐためです。

整合性はどこで守るか

収支の下書きを確定するときは、対象をロックしてから共通の収支処理を呼びます。台帳の履歴は追記のみとし、データベースのトリガーでも更新・削除を防ぎます。

テーブルの変更はFlywayで管理し、JPAは定義との一致を検証するだけにしています。APIには入出力用のDTOを設け、データベースの構造をそのまま返しません。

実装から学んだこと

今回扱ったのは、ログイン後の境界まで含めた設計です。認証が通っても、選択した世帯や参照先の口座を信用できるとは限りません。所有範囲を解決し、その範囲内でデータを取得するところまでを一続きにしました。

検証ではMySQLを使った結合テストと既存データの移行テストを用意しています。現在の品質確認はローカルの検証コマンドとリリース文書が中心で、CIは未導入です。

Codexには実装補助、文書整理、テスト追加を任せました。要件・技術選定・公開範囲は自分で決め、生成されたコードと文書をレビューしています。

既知の制約と今後の改善

  • ソースコードとデモは現在公開していません。外部環境でのデプロイ・切り戻しの検証も今後の課題です。
  • 追加の外部連携には、認証情報の設定と運用上の判断が必要です。
  • 画面は日英に対応していますが、入力内容の自動翻訳は未導入です。翻訳系APIの利用経験はあるものの、個人開発では費用面から見送っています。