長年動いてきた独自認証や既存ユーザDBを、いきなり捨てるわけにはいきません。User Storage SPIは、そのレガシーをKeycloakの背後に「繋いだまま」認証を統合し、時間をかけて安全に移行するための正攻法です。実装の勘所とキャッシュ・パスワード取り込みの落とし穴を、現場目線で整理します。
Keycloakは標準でLDAP/Active Directoryの連携機能を持ちますが、エンタープライズの現場で本当に困るのは「LDAPでもADでもない、独自の認証テーブルやレガシーAPI」です。合併で引き継いだ会員DB、10年もののパスワードハッシュ、社内独自のユーザ管理API — これらをどうKeycloakに繋ぐか。その答えがUser Storage SPIです。本稿では、既存DB/独自認証をKeycloakの背後に接続し、段階的に移行するための実装の勘所を、実務目線で整理します。
01User Storage SPIとは何を解決するのか
User Storage SPI(Service Provider Interface)は、Keycloakの内部データベース以外の場所にあるユーザ情報を、あたかもKeycloakのローカルユーザであるかのように扱うための拡張ポイントです。認証時にKeycloakは自前のユーザテーブルだけでなく、登録されたUser Storage Providerにも問い合わせます。既存システムを一切改修せず、参照元として繋ぐことができるのが最大の価値です。
公式のServer Developer Guideでは、この仕組みが「LDAP/ADはサポートするが、任意のカスタムユーザDBに対しても拡張を書ける」ものとして位置づけられています。設計の基本は2つのインターフェースです。
- UserStorageProviderFactory:ファクトリ。Keycloakはファクトリを単一インスタンスとして生成し、リクエストごとに
create()でProvider本体を作ります。Provider本体は軽量に保つのが鉄則です。 - UserStorageProvider:全実装が継承する基底インターフェース。ここに、必要な「能力(capability)」インターフェースを追加実装していきます。
02Capabilityインターフェース — 必要な分だけ実装する
User Storage SPIの設計思想は「できることだけを宣言する」です。基底のUserStorageProviderに対して、以下のcapabilityインターフェースを必要な分だけ足していきます。
- UserLookupProvider:ID/ユーザ名/メールでの単一ユーザ参照。認証の起点になるため、ほぼ必須です。
- CredentialInputValidator:パスワード等の資格情報検証。「このProviderがこの認証方式をサポートするか」と「検証結果」を返します。レガシー認証の心臓部です。
- UserQueryMethodsProvider:管理コンソールでの検索・一覧。運用者がユーザを探せるようにするため実務上は入れておきたい。なお件数取得(count)は別capabilityのUserCountMethodsProviderが担います。旧UserQueryProviderは検索・一覧・countを兼ねていましたが、現行版ではこの2つに分離されている点に注意してください。
- UserRegistrationProvider:外部ストレージへのユーザ追加・削除。参照専用で始めるなら実装しない選択も現実的です。
03Storage IDと federated(非import)モード
User Storage SPIには2つの動作モードがあります。1つはfederated(フェデレーション)モードで、ユーザをKeycloakのDBに取り込まず、参照のたびに外部ストレージへ問い合わせます。この場合、ユーザは「Storage ID」と呼ばれる合成IDで識別されます。形式は f:<プロバイダのコンポーネントID>:<外部システムでのユーザID> で、これによりKeycloakはどのProviderのどのユーザかを一意に解決します。
federatedモードの利点は、外部システムが常に正であること。取り込みによるデータ二重化が起きません。一方で、認証やトークン発行のたびに外部への問い合わせが走るため、外部システムのレイテンシと可用性がそのままKeycloakの応答性能に響きます。ここがキャッシュ設計(後述)の重要性に直結します。
04on-demand import — 段階移行の本命
もう1つのモードがon-demand import(オンデマンド取り込み)です。外部ストレージで見つかったユーザを、参照時にKeycloakのローカルDBへコピーして「ローカルユーザ化」します。以降そのユーザはKeycloakネイティブのユーザとして扱われ、属性やロールをKeycloak側で管理できるようになります。レガシーからの脱却を狙う移行案件では、こちらが本命です。
importモードで押さえるべきインターフェースは2つです。
- ImportedUserValidation:取り込み済みユーザが参照されるたびに呼ばれ、「外部の元データがまだ有効か」を検証します。外部で削除されたユーザのローカルコピーが残る事故を防ぐ要です。
nullを返せばそのユーザは無効と判断されます。 - ImportSynchronization:管理コンソールやスケジューラから、外部ストレージとの定期同期(full sync / changed-only sync)を駆動します。
05パスワードの段階取り込み — 移行の核心
レガシー移行で最も神経を使うのがパスワードです。ハッシュ方式が古い(独自salt付きMD5、bcryptの古いコスト等)場合、そのままKeycloakに移せません。ここでUser Storage SPIの真価が出ます。
戦略はこうです。CredentialInputValidatorでレガシー側のハッシュ検証ロジックをそのまま実装し、ユーザがログインに成功した瞬間、Keycloakネイティブのパスワードクレデンシャルとしてその平文を再ハッシュして保存します。ログイン成功時には平文パスワードが一時的にメモリ上に存在するため、これをKeycloak標準のPasswordCredentialModel(現行の推奨アルゴリズム)で保存し直すわけです。以降そのユーザはレガシー検証を経由せず、Keycloakネイティブで認証されます。
- ログイン頻度の高いユーザから自然に移行が進む(アクティブユーザ優先)。
- 一度も再ログインしない休眠ユーザだけが最後までレガシー検証に残る。ここは期限を切って強制リセット導線(要件次第で設計)に流すのが現実的です。
- 公式ガイドでも、質問応答クレデンシャル等について「パスワード同様にsalted hashで保持しうる」とされており、資格情報はKeycloakのセキュリティパターンに寄せる方針が推奨されています。
なお、パスワードの検証・更新まで扱う場合はCredentialInputUpdater等の関連インターフェースも視野に入りますが、バージョンによって扱いが変わるため、実装前に現行版のJavadocで最終確認してください。
06キャッシュ制御 — 性能と一貫性の綱引き
Keycloakは外部への往復を避けるため、User Storage Provider経由で取得したユーザをローカルにキャッシュします。federatedモードでは、このキャッシュが外部システムへの負荷とレイテンシを左右する生命線です。関連する要素は次の通りです。
- OnUserCache:ユーザがキャッシュされる際に呼ばれるコールバック。キャッシュエントリに独自データを載せたい場合に使います。
- CachePolicy:キャッシュの有効期限・失効戦略。DEFAULT / EVICT_DAILY / EVICT_WEEKLY / MAX_LIFESPAN / NO_CACHE などをProvider設定で選べます。外部の更新頻度に合わせて選定します。
- UserCacheインターフェース:
evict()で特定ユーザやレルム単位、clear()で全体のキャッシュ無効化ができます。外部側でユーザ情報が変わったら、Provider側から明示的にevictして一貫性を保ちます。
NO_CACHEで正しさを担保し、動作が固まってからEVICT_DAILY等に緩めるのが安全です。逆に、外部システムの負荷試験を省いてキャッシュ前提で本番投入すると、キャッシュ失効の谷でレガシーDBに突き刺さる負荷ピークを見落とします。Infinispanのキャッシュチューニングは別記事で扱っています(Infinispanキャッシュチューニング)。07パッケージングとデプロイ(Quarkus版基準)
現行のKeycloakはQuarkusベースが標準で、旧WildFly版は非推奨です。旧版のEARやjboss-cliを前提にした古い記事のデプロイ手順は使えないため注意してください。現行のConfiguring providersドキュメントに従うと、手順はシンプルです。
- ProviderをJARにパッケージし、ディストリビューションの
providersディレクトリへ配置する。サードパーティ依存も同ディレクトリに置く。 bin/kc.sh buildを実行し、Providerをビルド時に確定させる。これによりランタイム発見ではなく事前登録となり、起動が最適化される。- 削除する場合はJARを消して再度
buildを実行する。
—まとめ
User Storage SPIは、レガシー認証を「捨てずに繋ぐ」ための正攻法です。要点を整理します。
- まずread-only(UserLookupProvider + CredentialInputValidator)で繋ぎ、レガシーを正として始める。
- federatedかon-demand importかを、外部システムの可用性・移行ゴールで選ぶ。脱レガシーならimportが本命。
- パスワードはログイン成功時に再ハッシュしてネイティブ化し、アクティブユーザから自然移行させる。休眠ユーザは期限を切ってリセット導線へ。
- キャッシュは初期NO_CACHEで正しさ優先、固まってから緩める。外部負荷ピークを試験で確認する。
- デプロイはQuarkus版基準(providers配置+kc.sh build)。古いWildFly前提の手順に引きずられない。
EMWではKeycloakによるエンタープライズ認証基盤の実構築実績があります。具体的な移行設計は導入事例もご覧ください。バージョン依存の挙動は必ず現行版の公式ドキュメントで最終確認することをおすすめします。