最新の安定バージョンについては、Spring Security 6.3.1 を使用してください!

Spring Security FAQ

一般的な質問

Spring Security はすべてのアプリケーションセキュリティ要件を処理しますか?

Spring Security は、認証と認可の要件に対応する非常に柔軟なフレームワークを提供しますが、その範囲外の安全なアプリケーションを構築するには、他にも多くの考慮事項があります。Web アプリケーションは、知っておく必要があるあらゆる種類の攻撃に対して脆弱です。できれば開発を始める前に、最初から念頭に置いて設計およびコーディングできるようにしてください。OWASP Web サイト (英語) をチェックして、Web アプリケーション開発者が直面している主な課題とそれらに対して使用できる対策について確認してください。

web.xml セキュリティを使用しないのはなぜですか?

Spring に基づいたエンタープライズアプリケーションを開発していると仮定しましょう。通常、対処する必要があるセキュリティ上の懸念は 4 つあります: 認証、Web リクエストセキュリティ、サービスレイヤーセキュリティ(ビジネスロジックを実装するメソッド)、およびドメインオブジェクトインスタンスセキュリティ(つまり、異なるドメインオブジェクトには異なるアクセス許可があります)。これらの典型的な要件を念頭に置いて:

  1. 認証 : サーブレット仕様は、認証へのアプローチを提供します。ただし、通常はコンテナー固有の「レルム」設定の編集が必要な認証を実行するようにコンテナーを構成する必要があります。これにより、移植性のない構成になり、コンテナーの認証インターフェースを実装するために実際の Java クラスを記述する必要がある場合、さらに移植性のないものになります。Spring Security を使用すると、WAR レベルまで完全な移植性を実現できます。また、Spring Security は、実績のある認証プロバイダーとメカニズムの選択肢を提供します。つまり、デプロイ時に認証アプローチを切り替えることができます。これは、未知のターゲット環境で動作する必要がある製品を作成するソフトウェアベンダーにとって特に価値があります。

  2. Web リクエストのセキュリティ : サーブレット仕様は、リクエスト URI を保護するアプローチを提供します。ただし、これらの URI は、サーブレット仕様自体の制限された URI パス形式でのみ表現できます。Spring Security は、はるかに包括的なアプローチを提供します。たとえば、Ant パスまたは正規表現を使用でき、単にリクエストされたページ以外の URI の部分を考慮することができ(たとえば、HTTP GET パラメーターを考慮することができます)、構成データの独自のランタイムソースを実装できます。つまり、webapp の実際の実行中に、Web リクエストのセキュリティを動的に変更できます。

  3. サービス層とドメインオブジェクトのセキュリティ : サービス層セキュリティまたはドメインオブジェクトインスタンスセキュリティのサーブレット仕様にサポートがないことは、多層アプリケーションの重大な制限を表しています。通常、開発者はこれらの要件を無視するか、MVC コントローラーコード内(またはさらに悪いことにビュー内)にセキュリティロジックを実装します。このアプローチには重大な欠点があります。

    1. 関心事の分離 : 認可は横断的な関心事であり、そのように実装する必要があります。認可コードを実装する MVC コントローラーまたはビューは、コントローラーと認可ロジックの両方をテストすることをより困難にし、デバッグすることをより難しくし、多くの場合、コードの重複につながります。

    2. リッチクライアントと Web サービスのサポート : 追加のクライアント型を最終的にサポートする必要がある場合、Web レイヤーに埋め込まれた認証コードは再利用できません。Spring リモーティングエクスポーターは、サービスレイヤー Bean のみをエクスポートする(MVC コントローラーはエクスポートしない)ことを考慮してください。そのため、多数のクライアント型をサポートするには、サービスレイヤーに認証ロジックを配置する必要があります。

    3. レイヤーリングの課題 : MVC コントローラーまたはビューは、サービスレイヤーメソッドまたはドメインオブジェクトインスタンスに関する認可決定を実装するための、単に誤ったアーキテクチャレイヤーです。プリンシパルはサービス層に渡されて認可決定を行うことができますが、そうすると、すべてのサービス層メソッドに追加の引数が導入されます。より洗練されたアプローチは、ThreadLocal を使用してプリンシパルを保持することです。ただし、専用のセキュリティフレームワークを使用するだけで、より経済的に(費用対効果で)なるまで開発時間が長くなる可能性があります。

    4. 認証コードの品質 : Web フレームワークでは、「正しいことをするのが簡単になり、間違ったことをするのが難しくなる」とよく言われます。セキュリティフレームワークは、さまざまな目的のために抽象的な方法で設計されているため、同じです。独自の認証コードを最初から作成しても、フレームワークが提供する「設計チェック」は提供されません。また、社内の認証コードには一般に、広範囲にわたるデプロイ、ピアレビュー、新しいバージョンから生じる改善が欠落しています。

単純なアプリケーションの場合、サーブレット仕様のセキュリティで十分な場合があります。Web コンテナーの移植性、構成要件、制限された Web リクエストセキュリティの柔軟性、および存在しないサービスレイヤーとドメインオブジェクトインスタンスのセキュリティのコンテキスト内で検討すると、開発者が代替ソリューションをよく検討する理由が明らかになります。

どの Java および Spring Framework バージョンが必要ですか?

Spring Security 3.0 と 3.1 には、少なくとも JDK 1.5 が必要であり、最低でも Spring 3.0.3 が必要です。理想的には、問題を回避するために最新のリリースバージョンを使用する必要があります。

Spring Security 2.0.x には、1.4 の最小 JDK バージョンが必要であり、Spring 2.0.x に対してビルドされます。また、Spring 2.5.x を使用するアプリケーションとも互換性があります。

Spring Security を初めて使用します。HTTPS を介した CAS シングルサインオンをサポートし、特定の URL に対して基本認証をローカルで許可し、複数のバックエンドユーザー情報ソース(LDAP および JDBC)に対して認証するアプリケーションを構築する必要があります。見つけたいくつかの構成ファイルをコピーしましたが、機能しません。

何が間違っているのでしょうか?

または、代替の複雑なシナリオで置き換えます。…

現実的には、使用してアプリケーションを正常に構築する前に、使用する予定のテクノロジーを理解する必要があります。セキュリティは複雑です。ログインフォームを使用して簡単な構成を設定し、Spring Security の名前空間を使用してハードコードされたユーザーを設定することは、かなり簡単です。バックアップされた JDBC データベースの使用への移行も簡単です。しかし、このような複雑なデプロイシナリオに直接ジャンプしようとすると、ほぼ間違いなくイライラします。CAS のようなシステムをセットアップし、LDAP サーバーを構成し、SSL 証明書を適切にインストールするために必要な学習曲線には大きなジャンプがあります。一度に 1 つのステップを踏む必要があります。

Spring Security の観点から、最初にするべきことは、Web サイトの「入門」ガイドに従うことです。これにより、一連の手順を実行して、フレームワークがどのように動作するかを理解することができます。慣れていない他の技術を使用している場合は、いくつかの研究を行い、分離して使用できることを確認してから、複雑なシステムで組み合わせてください。

よくある問題

  1. 認証

  2. セッション管理

  3. その他

これは、認証が失敗したことを意味します。攻撃者がアカウント名またはパスワードを推測するのに役立つ可能性のある詳細を提供しないことをお勧めするため、理由はわかりません。

これはまた、フォーラムでこの質問をした場合、追加情報を提供しない限り回答を得られないことを意味します。他の課題と同様に、デバッグログからの出力を確認し、例外スタックトレースと関連メッセージをメモしてください。デバッガーでコードをステップ実行して、認証が失敗した場所と理由を確認します。アプリケーションの外部で認証設定を実行するテストケースを作成します。多くの場合、失敗の原因は、データベースに保存されているパスワードデータとユーザーが入力したパスワードデータの違いにあります。ハッシュ化されたパスワードを使用している場合は、データベースに保存されている値が、アプリケーションで設定された PasswordEncoder によって生成された値と正確に同じであることを確認してください。

ログインしようとすると、アプリケーションが「無限ループ」に入ります。

無限ループとログインページへのリダイレクトに関する一般的なユーザーの問題は、誤ってログインページを「保護された」リソースとして構成したことが原因です。セキュリティフィルターチェーンから除外するか、ROLE_ANONYMOUS を必要とするようにマークを付けることにより、構成がログインページへの匿名アクセスを許可していることを確認してください。

AccessDecisionManager に AuthenticatedVoter が含まれている場合、属性 "IS_AUTHENTICATED_ANONYMOUSLY" を使用できます。これは、標準のネームスペース構成セットアップを使用している場合、自動的に利用可能です。

Spring Security 2.0.1 以降、名前空間ベースの設定を使用している場合、アプリケーションコンテキストのロード時にチェックが行われ、ログインページが保護されているように見える場合は警告メッセージがログに記録されます。

「アクセスが拒否されました(ユーザーは匿名です);」というメッセージで例外が発生します。どうしてでしょうか?

これは、匿名ユーザーが保護されたリソースに初めてアクセスしようとしたときに発生するデバッグレベルのメッセージです。

DEBUG [ExceptionTranslationFilter] - Access is denied (user is anonymous); redirecting to authentication entry point
org.springframework.security.AccessDeniedException: Access is denied
at org.springframework.security.vote.AffirmativeBased.decide(AffirmativeBased.java:68)
at org.springframework.security.intercept.AbstractSecurityInterceptor.beforeInvocation(AbstractSecurityInterceptor.java:262)

これは正常なことであり、心配する必要はありません。

アプリケーションからログアウトした後でも、保護されたページが表示されるのはなぜですか?

これの最も一般的な理由は、ブラウザーがページをキャッシュしており、ブラウザーのキャッシュから取得されているコピーが表示されていることです。ブラウザーが実際にリクエストを送信しているかどうかを確認して、これを確認します(サーバーアクセスログ、デバッグログを確認するか、Firefox の「改ざんデータ」などの適切なブラウザーデバッグプラグインを使用します)。これは Spring Security とは関係ありません。適切な Cache-Control レスポンスヘッダーを設定するようにアプリケーションまたはサーバーを構成する必要があります。SSL リクエストはキャッシュされないことに注意してください。

「SecurityContext で認証オブジェクトが見つかりませんでした」というメッセージで例外が発生します。どうしてでしょうか?

これは、匿名ユーザーが保護されたリソースに初めてアクセスしようとしたときに、フィルターチェーン構成に AnonymousAuthenticationFilter がない場合に発生する別のデバッグレベルメッセージです。

DEBUG [ExceptionTranslationFilter] - Authentication exception occurred; redirecting to authentication entry point
org.springframework.security.AuthenticationCredentialsNotFoundException:
							An Authentication object was not found in the SecurityContext
at org.springframework.security.intercept.AbstractSecurityInterceptor.credentialsNotFound(AbstractSecurityInterceptor.java:342)
at org.springframework.security.intercept.AbstractSecurityInterceptor.beforeInvocation(AbstractSecurityInterceptor.java:254)

これは正常なことであり、心配する必要はありません。

LDAP 認証が機能しません。

構成の何が問題になっていますか?

LDAP ディレクトリのアクセス許可では、多くの場合、ユーザーのパスワードを読み取れないことに注意してください。そのため、Spring Security が保存されたパスワードとユーザーが送信したパスワードを比較する UserDetailsService とは何ですか? を使用することはできません。最も一般的なアプローチは、LDAP プロトコル [Wikipedia] でサポートされる操作の 1 つである LDAP「バインド」を使用することです。このアプローチでは、Spring Security はユーザーとしてディレクトリへの認証を試みることによりパスワードを検証します。

LDAP 認証の最も一般的な問題は、ディレクトリサーバーのツリー構造と構成に関する知識の不足です。これは企業によって異なるため、自分で調べる必要があります。Spring Security LDAP 構成をアプリケーションに追加する前に、標準の Java LDAP コードを使用して(Spring Security を使用せずに)簡単なテストを作成し、それが最初に機能することを確認することをお勧めします。例: ユーザーを認証するには、次のコードを使用できます。

  • Java

  • Kotlin

@Test
public void ldapAuthenticationIsSuccessful() throws Exception {
		Hashtable<String,String> env = new Hashtable<String,String>();
		env.put(Context.SECURITY_AUTHENTICATION, "simple");
		env.put(Context.SECURITY_PRINCIPAL, "cn=joe,ou=users,dc=mycompany,dc=com");
		env.put(Context.PROVIDER_URL, "ldap://mycompany.com:389/dc=mycompany,dc=com");
		env.put(Context.SECURITY_CREDENTIALS, "joespassword");
		env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");

		InitialLdapContext ctx = new InitialLdapContext(env, null);

}
@Test
fun ldapAuthenticationIsSuccessful() {
    val env = Hashtable<String, String>()
    env[Context.SECURITY_AUTHENTICATION] = "simple"
    env[Context.SECURITY_PRINCIPAL] = "cn=joe,ou=users,dc=mycompany,dc=com"
    env[Context.PROVIDER_URL] = "ldap://mycompany.com:389/dc=mycompany,dc=com"
    env[Context.SECURITY_CREDENTIALS] = "joespassword"
    env[Context.INITIAL_CONTEXT_FACTORY] = "com.sun.jndi.ldap.LdapCtxFactory"
    val ctx = InitialLdapContext(env, null)
}

セッション管理

セッション管理の課題は、フォーラムの質問の一般的なソースです。Java Web アプリケーションを開発している場合、サーブレットコンテナーとユーザーのブラウザー間でセッションがどのように維持されるかを理解する必要があります。また、セキュア Cookie と非セキュア Cookie の違いと、HTTP/HTTPS を使用して 2 つの間を切り替えることの意味を理解する必要があります。Spring Security は、セッションの維持またはセッション ID の提供とは関係ありません。これは、サーブレットコンテナーによって完全に処理されます。

Spring Security の同時セッション制御を使用して、ユーザーが一度に複数回ログインするのを防ぎます。

ログイン後に別のブラウザーウィンドウを開いても、再度ログインすることはできません。なぜ複数回ログインできるのですか?

ブラウザーは通常、ブラウザーインスタンスごとに 1 つのセッションを維持します。一度に 2 つの別々のセッションを持つことはできません。別のウィンドウまたはタブで再度ログインすると、同じセッションで再認証するだけです。サーバーは、タブ、ウィンドウ、ブラウザーインスタンスについて何も知りません。表示されるのは HTTP リクエストだけであり、含まれている JSESSIONIDCookie の値に従って特定のセッションに関連付けます。ユーザーがセッション中に認証を行うと、Spring Security の同時セッション制御は、ユーザーが持っている他の認証済みセッションの数をチェックします。それらが同じセッションですでに認証されている場合、再認証は効果がありません。

Spring Security で認証すると、セッション ID が変わるのはなぜですか?

デフォルト構成では、Spring Security はユーザーが認証されるとセッション ID を変更します。Servlet 3.1 以降のコンテナーを使用している場合、セッション ID は単に変更されます。古いコンテナーを使用している場合、Spring Security は既存のセッションを無効にし、新しいセッションを作成して、セッションデータを新しいセッションに転送します。この方法でセッション識別子を変更すると、「セッション固定」攻撃を防ぐことができます。この詳細については、オンラインおよびリファレンスマニュアルを参照してください。

Tomcat(または他のサーブレットコンテナー)を使用しており、ログインページで HTTPS を有効にし、その後 HTTP に戻りました。

効かない - 認証後、ログインページに戻ります。

これは、セッション Cookie が「セキュア」とマークされている HTTPS で作成されたセッションは、その後 HTTP で使用できないために発生します。ブラウザーは Cookie をサーバーに返送せず、セッションの状態はすべて失われます(セキュリティコンテキスト情報を含む)。セッション Cookie はセキュアとしてマークされないため、最初に HTTP でセッションを開始すると機能します。ただし、Spring Security のセッション固定保護は、通常はセキュアフラグを使用して、新しいセッション ID Cookie がユーザーのブラウザーに送り返されるため、これに干渉する可能性があります。これを回避するために、セッション固定保護を無効にすることができますが、新しいサーブレットコンテナーでは、セキュアフラグを使用しないようにセッション Cookie を構成することもできます。HTTP を使用するアプリケーションは中間者攻撃に対して脆弱であるため、HTTP と HTTPS の切り替えは一般的には良いアイデアではないことに注意してください。本当に安全にするために、ユーザーは HTTPS でサイトへのアクセスを開始し、ログアウトするまでサイトの使用を続ける必要があります。HTTP 経由でアクセスしたページから HTTPS リンクをクリックすることは、潜在的に危険です。さらに説得力が必要な場合は、sslstrip [GitHub] (英語) のようなツールをチェックしてください。

HTTP と HTTPS を切り替えていませんが、セッションがまだ失われています

セッションは、セッション Cookie を交換するか、URL に jsessionid パラメーターを追加することで維持されます(JSTL を使用して URL を出力する場合、または URL で HttpServletResponse.encodeUrl を呼び出す場合(リダイレクトの前など)。jsessionid を含めるように URL を書き換えていない場合、セッションは失われます。URL のセッション情報を公開しないため、セキュリティ上の理由から Cookie の使用が推奨されます。

同時セッション制御サポートを使用しようとしていますが、ログアウトしていて許可されたセッションを超えていないと確信しても、再度ログインすることはできません。

web.xml ファイルにリスナーを追加したことを確認してください。セッションが破棄されたときに Spring Security セッションレジストリに通知されるようにすることが不可欠です。これがないと、セッション情報はレジストリから削除されません。

<listener>
		<listener-class>org.springframework.security.web.session.HttpSessionEventPublisher</listener-class>
</listener>

Spring Security は、create-session 属性を never に設定することにより、設定していないにもかかわらず、どこかにセッションを作成しています。

これは通常、ユーザーのアプリケーションがどこかでセッションを作成しているが、それを認識していないことを意味します。最も一般的な犯人は JSP です。多くの人は、JSP がデフォルトでセッションを作成することを認識していません。JSP がセッションを作成しないようにするには、ページの上部にディレクティブ <%@ page session="false" %> を追加します。

セッションの作成場所の解決に問題がある場合は、デバッグコードを追加して場所を追跡できます。これを行う 1 つの方法は、javax.servlet.http.HttpSessionListener をアプリケーションに追加し、sessionCreated メソッドで Thread.dumpStack() を呼び出すことです。

POST を実行すると 403 Forbidden が発生します

HTTP 403 Forbidden が HTTP POST に対して返されるが、HTTP GET に対しては機能する場合、課題は CSRF に関連している可能性があります。CSRF トークンを提供するか、CSRF 保護を無効にします(非推奨)。

RequestDispatcher を使用してリクエストを別の URL に転送していますが、セキュリティ上の制約は適用されていません。

デフォルトでは、フィルターは転送または組み込みに適用されません。セキュリティフィルターを転送やインクルードに実際に適用する場合は、<filter-mapping> の子要素である <dispatcher> 要素を使用して web.xml でこれらを明示的に構成する必要があります。

Spring Security の <global-method-security> 要素をアプリケーションコンテキストに追加しましたが、Spring MVC コントローラー Bean (Struts アクションなど) にセキュリティアノテーションを追加しても、効果がないようです。

Spring Web アプリケーションでは、ディスパッチャーサーブレットの Spring MVC Bean を保持するアプリケーションコンテキストは、多くの場合、メインアプリケーションコンテキストから分離されています。多くの場合、これは myapp-servlet.xml というファイルで定義されます。"myapp" は web.xml で Spring DispatcherServlet に割り当てられた名前です。アプリケーションは複数の DispatcherServlet を持つことができ、それぞれが独自の分離されたアプリケーションコンテキストを持ちます。これらの「子」コンテキストの Bean は、アプリケーションの他の部分からは見えません。「親」アプリケーションコンテキストは、web.xml で定義した ContextLoaderListener によってロードされ、すべての子コンテキストに表示されます。通常、この親コンテキストは、<global-method-security> 要素を含むセキュリティ構成を定義する場所です)。結果として、これらの Web Bean のメソッドに適用されるセキュリティ制約は適用されません。これは、Bean が DispatcherServlet コンテキストから見えないためです。<global-method-security> 宣言を Web コンテキストに移動するか、保護する Bean をメインアプリケーションコンテキストに移動する必要があります。

一般に、個々の Web コントローラーではなく、サービスレイヤーでメソッドセキュリティを適用することをお勧めします。

確実に認証されたユーザーがいますが、リクエスト中に SecurityContextHolder にアクセスしようとすると、認証が null になります。

ユーザー情報が表示されないのはなぜですか?

URL パターンに一致する <intercept-url> 要素の属性 filters='none' を使用してセキュリティフィルターチェーンからリクエストを除外した場合、SecurityContextHolder はそのリクエストに入力されません。デバッグログをチェックして、リクエストがフィルターチェーンを通過しているかどうかを確認します。(デバッグログを読んでいます。よね? )。

認可 JSP タグは、URL 属性を使用するときにメソッドセキュリティアノテーションを考慮しません。

コントローラーがヘッダーや現在のユーザーなどに依存して呼び出すメソッドを決定できるため、どの URL がどのコントローラーエンドポイントにマッピングされるかをリバースエンジニアリングできないため、<sec:authorize> で url 属性を使用する場合、メソッドセキュリティはリンクを隠しません。

Spring Security アーキテクチャに関する質問

どのパッケージクラス X が含まれているかを知るにはどうすればよいですか

クラスを見つける最良の方法は、Spring Security ソースを IDE にインストールすることです。ディストリビューションには、プロジェクトが分割される各モジュールのソース jar が含まれています。これらをプロジェクトのソースパスに追加すると、Spring Security クラス(Eclipse の Ctrl-Shift-T)に直接移動できます。また、これによりデバッグが容易になり、例外が発生したコードを直接見て、そこで何が起こっているのかを確認することで、例外のトラブルシューティングを行うことができます。

名前空間要素は、従来の Bean 構成にどのようにマッピングされますか?

リファレンスガイドの名前空間の付録に、名前空間によって作成される Bean の一般的な概要があります。blog.springsource.com (英語) には、「Spring Security 名前空間の背後にある」という詳細なブログ記事もあります。詳細を知りたい場合、コードは Spring Security 3.0 ディストリビューション内の spring-security-config モジュールにあります。最初に、標準の Spring Framework リファレンスドキュメントの名前空間解析に関する章を読む必要があります。

"ROLE_" とは何を意味し、なぜロール名にそれが必要なのですか?

Spring Security には、投票者ベースのアーキテクチャがあります。つまり、一連の AccessDecisionVoter によってアクセス決定が行われます。投票者は、セキュリティで保護されたリソース(メソッド呼び出しなど)に指定された「構成属性」に基づいて行動します。このアプローチでは、すべての属性がすべての投票者に関連するわけではなく、投票者は、属性値に基づいて、いつ属性を無視するか(棄権)、いつアクセスを許可または拒否するかを知る必要があります。最も一般的な投票者は RoleVoter であり、これはデフォルトで "ROLE_" プレフィックスを持つ属性を見つけるたびに投票します。これは、属性( "ROLE_USER" など)と、現在のユーザーが割り当てられているオーソリティの名前を簡単に比較します。一致するものが見つかった場合( "ROLE_USER" と呼ばれるオーソリティがあります)、アクセスを許可するために投票し、そうでない場合はアクセスを拒否するために投票します。

RoleVoter の rolePrefix プロパティを設定することにより、プレフィックスを変更できます。アプリケーションでロールのみを使用する必要があり、他のカスタム投票者を必要としない場合、プレフィックスを空の文字列に設定できます。その場合、RoleVoter はすべての属性をロールとして扱います。

Spring Security で動作するためにアプリケーションに追加する依存関係を知るにはどうすればよいですか?

これは、使用している機能や開発しているアプリケーションの種類によって異なります。Spring Security 3.0 を使用すると、プロジェクト jar は機能の明確に異なる領域に分割されるため、アプリケーション要件から必要な Spring Security jar を簡単に特定できます。すべてのアプリケーションには spring-security-core jar が必要です。Web アプリケーションを開発している場合は、spring-security-web jar が必要です。セキュリティ名前空間構成を使用している場合は、spring-security-config jar が必要です。LDAP サポートには、spring-security-ldap jar などが必要です。

サードパーティの jar の場合、状況は必ずしもそれほど明白ではありません。良い出発点は、事前に構築されたサンプルアプリケーションの WEB-INF/lib ディレクトリの 1 つからコピーすることです。基本的なアプリケーションの場合、チュートリアルサンプルから開始できます。組み込みテストサーバーで LDAP を使用する場合は、LDAP サンプルを開始点として使用します。リファレンスマニュアルには、各 Spring Security モジュールの第 1 レベルの依存関係をリストした付録も含まれており、それらがオプションであるかどうか、それらが何のために必要かについての情報が含まれています。

maven を使用してプロジェクトをビルドする場合、pom.xml に適切な Spring Security モジュールを依存関係として追加すると、フレームワークに必要なコア jar が自動的にプルされます。Spring Security POM ファイルで「オプション」とマークされているものは、必要に応じて独自の pom.xml ファイルに追加する必要があります。

組み込み ApacheDS LDAP サーバーを実行するには、どのような依存関係が必要ですか?

Maven を使用している場合は、pom の依存関係に以下を追加する必要があります。

<dependency>
		<groupId>org.apache.directory.server</groupId>
		<artifactId>apacheds-core</artifactId>
		<version>1.5.5</version>
		<scope>runtime</scope>
</dependency>
<dependency>
		<groupId>org.apache.directory.server</groupId>
		<artifactId>apacheds-server-jndi</artifactId>
		<version>1.5.5</version>
		<scope>runtime</scope>
</dependency>

他の必要な jar は推移的に取り込む必要があります。

UserDetailsService とは何ですか?

UserDetailsService は、ユーザーアカウントに固有のデータを読み込むための DAO インターフェースです。フレームワーク内の他のコンポーネントで使用するためにそのデータをロードする他の機能はありません。ユーザーを認証する責任はありません。ユーザー名 / パスワードの組み合わせによるユーザーの認証は、ほとんどの場合、DaoAuthenticationProvider によって実行されます。DaoAuthenticationProvider には UserDetailsService が注入され、ユーザーのパスワード(およびその他のデータ)をロードして送信された値と比較できます。LDAP を使用している場合、このアプローチは機能しない可能性があることに注意してください。

認証プロセスをカスタマイズしたい場合は、AuthenticationProvider を自分で実装する必要があります。Spring Security 認証と Google App Engine を統合する例については、このブログ記事 (英語) を参照してください。

一般的な "Howto" リクエスト

ユーザー名以外の情報でログインする必要があります。

追加のログインフィールド(会社名など)のサポートを追加するにはどうすればよいですか?

この質問は Spring Security フォーラムに繰り返し出てくるため、アーカイブを検索する(または google で検索する)ことで、より多くの情報を見つけることができます。

送信されたログイン情報は、UsernamePasswordAuthenticationFilter のインスタンスによって処理されます。このクラスをカスタマイズして、追加のデータフィールドを処理する必要があります。一つの選択肢は、独自のカスタマイズされた認証トークンクラス (標準的な UsernamePasswordAuthenticationToken ではなく) を使用することであり、もう一つの選択肢は、余分なフィールドをユーザー名 (たとえば、セパレータとして ":" を使用) と連結し、UsernamePasswordAuthenticationToken の username プロパティに渡すことです。

また、実際の認証プロセスをカスタマイズする必要があります。たとえば、カスタム認証トークンクラスを使用している場合、AuthenticationProvider を記述してそれを処理する(または標準の DaoAuthenticationProvider を継承する)必要があります。フィールドを連結している場合、独自の UserDetailsService を実装して、フィールドを分割し、認証用の適切なユーザーデータをロードできます。

リクエストされた URL のフラグメント値のみが異なる(たとえば、/ foo#bar と /foo#blah? の)異なる intercept-url 制約を適用する方法

フラグメントはブラウザーからサーバーに送信されないため、これを行うことはできません。上記の URL は、サーバーの観点からは同一です。これは GWT ユーザーからのよくある質問です。

UserDetailsService でユーザーの IP アドレス(または他の Web リクエストデータ)にアクセスするにはどうすればよいですか?

インターフェースに提供される情報はユーザー名のみであるため、明らかに(スレッドローカル変数のようなものに頼ることなく)できません。UserDetailsService を実装する代わりに、AuthenticationProvider を直接実装し、提供された Authentication トークンから情報を抽出する必要があります。

標準の Web セットアップでは、Authentication オブジェクトの getDetails() メソッドは WebAuthenticationDetails のインスタンスを返します。追加情報が必要な場合は、使用している認証フィルターにカスタム AuthenticationDetailsSource を挿入できます。<form-login> 要素などで名前空間を使用している場合、この要素を削除し、明示的に設定された UsernamePasswordAuthenticationFilter を指す <custom-filter> 宣言で置き換える必要があります。

UserDetailsService から HttpSession にアクセスするにはどうすればよいですか?

UserDetailsService はサーブレット API を認識しないため、できません。カスタムユーザーデータを保存する場合は、返される UserDetails オブジェクトをカスタマイズする必要があります。これは、スレッドローカル SecurityContextHolder を介して、いつでもアクセスできます。SecurityContextHolder.getContext().getAuthentication().getPrincipal() を呼び出すと、このカスタムオブジェクトが返されます。

セッションに本当にアクセスする必要がある場合は、Web 層をカスタマイズしてアクセスする必要があります。

UserDetailsService でユーザーのパスワードにアクセスするにはどうすればよいですか?

できません(できません)。おそらくその目的を誤解しているでしょう。上記の "UserDetailsService とは何ですか? " を参照してください。

アプリケーション内でセキュアな URL を動的に定義するにはどうすればよいですか

保護された URL とセキュリティメタデータ属性の間のマッピングを、アプリケーションコンテキストではなくデータベースに保存する方法についてよく尋ねられます。

最初に自問すべきことは、本当にこれを行う必要があるかどうかです。アプリケーションで保護が必要な場合、定義されたポリシーに基づいてセキュリティを徹底的にテストすることも必要です。本番環境に展開する前に、監査と受け入れテストが必要になる場合があります。セキュリティを重視する組織は、構成データベースの 1 行または 2 行を変更することにより、実行時にセキュリティ設定を変更できるようにすることで、勤勉なテストプロセスの利点をすぐに一掃できることに注意する必要があります。これを考慮した場合(おそらくアプリケーション内で複数のセキュリティレイヤーを使用している場合)、Spring Security を使用すると、セキュリティメタデータのソースを完全にカスタマイズできます。必要に応じて完全に動的にすることができます。

メソッドおよび Web セキュリティは、AbstractSecurityInterceptor のサブクラスによって保護されています。AbstractSecurityInterceptor は、特定のメソッドまたはフィルター呼び出しのメタデータを取得する SecurityMetadataSource で構成されています。Web セキュリティの場合、インターセプタークラスは FilterSecurityInterceptor であり、マーカーインターフェース FilterInvocationSecurityMetadataSource を使用します。動作する「保護されたオブジェクト」型は FilterInvocation です。使用されるデフォルトの実装(名前空間 <http> 内およびインターセプターを明示的に構成する場合の両方)は、インメモリマップに URL パターンのリストと「構成属性」(ConfigAttribute のインスタンス)の対応するリストを格納します。

代替ソースからデータをロードするには、FilterSecurityInterceptor Bean をカスタマイズするために、明示的に宣言されたセキュリティフィルターチェーン(通常 Spring Security の FilterChainProxy)を使用する必要があります。名前空間は使用できません。次に、特定の FilterInvocation [ 1 ] で必要に応じて FilterInvocationSecurityMetadataSource を実装してデータをロードします。非常に基本的なアウトラインは次のようになります。

  • Java

  • Kotlin

	public class MyFilterSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {

		public List<ConfigAttribute> getAttributes(Object object) {
			FilterInvocation fi = (FilterInvocation) object;
				String url = fi.getRequestUrl();
				String httpMethod = fi.getRequest().getMethod();
				List<ConfigAttribute> attributes = new ArrayList<ConfigAttribute>();

				// Lookup your database (or other source) using this information and populate the
				// list of attributes

				return attributes;
		}

		public Collection<ConfigAttribute> getAllConfigAttributes() {
			return null;
		}

		public boolean supports(Class<?> clazz) {
			return FilterInvocation.class.isAssignableFrom(clazz);
		}
	}
class MyFilterSecurityMetadataSource : FilterInvocationSecurityMetadataSource {
    override fun getAttributes(securedObject: Any): List<ConfigAttribute> {
        val fi = securedObject as FilterInvocation
        val url = fi.requestUrl
        val httpMethod = fi.request.method

        // Lookup your database (or other source) using this information and populate the
        // list of attributes
        return ArrayList()
    }

    override fun getAllConfigAttributes(): Collection<ConfigAttribute>? {
        return null
    }

    override fun supports(clazz: Class<*>): Boolean {
        return FilterInvocation::class.java.isAssignableFrom(clazz)
    }
}

詳細については、DefaultFilterInvocationSecurityMetadataSource のコードを参照してください。

LDAP に対して認証するが、データベースからユーザーロールをロードするにはどうすればよいですか?

LdapAuthenticationProvider Bean(Spring Security で通常の LDAP 認証を処理する)は、認証を実行するものと、それぞれ LdapAuthenticator および LdapAuthoritiesPopulator と呼ばれるユーザー権限をロードするものの 2 つの別個の戦略インターフェースで構成されます。DefaultLdapAuthoritiesPopulator は、LDAP ディレクトリからユーザー権限をロードし、これらを取得する方法を指定できるさまざまな構成パラメーターを持っています。

代わりに JDBC を使用するには、スキーマに適した SQL を使用して、自分でインターフェースを実装できます。

  • Java

  • Kotlin

	public class MyAuthoritiesPopulator implements LdapAuthoritiesPopulator {
		@Autowired
		JdbcTemplate template;

		List<GrantedAuthority> getGrantedAuthorities(DirContextOperations userData, String username) {
			return template.query("select role from roles where username = ?",
					new String[] {username},
					new RowMapper<GrantedAuthority>() {
				 /**
				 *  We're assuming here that you're using the standard convention of using the role
				 *  prefix "ROLE_" to mark attributes which are supported by Spring Security's RoleVoter.
				 */
				@Override
				public GrantedAuthority mapRow(ResultSet rs, int rowNum) throws SQLException {
					return new SimpleGrantedAuthority("ROLE_" + rs.getString(1));
				}
			});
		}
	}
class MyAuthoritiesPopulator : LdapAuthoritiesPopulator {
    @Autowired
    lateinit var template: JdbcTemplate

    override fun getGrantedAuthorities(userData: DirContextOperations, username: String): MutableList<GrantedAuthority?> {
        return template.query("select role from roles where username = ?",
            arrayOf(username)
        ) { rs, _ ->
            /**
             * We're assuming here that you're using the standard convention of using the role
             * prefix "ROLE_" to mark attributes which are supported by Spring Security's RoleVoter.
             */
            SimpleGrantedAuthority("ROLE_" + rs.getString(1))
        }
    }
}

次に、この型の Bean をアプリケーションコンテキストに追加し、LdapAuthenticationProvider に挿入します。これについては、リファレンスマニュアルの LDAP の章の明示的な Spring Bean を使用した LDAP の設定に関するセクションで説明されています。この場合、構成に名前空間を使用できないことに注意してください。関連するクラスとインターフェースについては、Javadoc も参照してください。

名前空間によって作成された Bean のプロパティを変更したいのですが、スキーマにはそれをサポートするものが何もありません。

名前空間の使用を放棄する前に何ができますか?

名前空間の機能は意図的に制限されているため、プレーン Bean で実行できるすべての機能をカバーしているわけではありません。Bean の変更や別の依存関係の挿入など、単純なことを実行したい場合は、構成に BeanPostProcessor を追加することでこれを行うことができます。詳細については、Spring リファレンスマニュアルを参照してください。これを行うには、どの Bean が作成されるかについて少し知る必要があるため、名前空間が Spring Bean にどのようにマップされるかに関する上記の質問のブログ記事も読む必要があります。

通常、必要な機能を BeanPostProcessor の postProcessBeforeInitialization メソッドに追加します。UsernamePasswordAuthenticationFilter で使用される AuthenticationDetailsSource (form-login 要素で作成)をカスタマイズするとします。リクエストから CUSTOM_HEADER と呼ばれる特定のヘッダーを抽出し、ユーザーの認証中にそれを使用する必要があります。プロセッサークラスは次のようになります。

  • Java

  • Kotlin

public class CustomBeanPostProcessor implements BeanPostProcessor {

		public Object postProcessAfterInitialization(Object bean, String name) {
				if (bean instanceof UsernamePasswordAuthenticationFilter) {
						System.out.println("********* Post-processing " + name);
						((UsernamePasswordAuthenticationFilter)bean).setAuthenticationDetailsSource(
										new AuthenticationDetailsSource() {
												public Object buildDetails(Object context) {
														return ((HttpServletRequest)context).getHeader("CUSTOM_HEADER");
												}
										});
				}
				return bean;
		}

		public Object postProcessBeforeInitialization(Object bean, String name) {
				return bean;
		}
}
class CustomBeanPostProcessor : BeanPostProcessor {
    override fun postProcessAfterInitialization(bean: Any, name: String): Any {
        if (bean is UsernamePasswordAuthenticationFilter) {
            println("********* Post-processing $name")
            bean.setAuthenticationDetailsSource(
                AuthenticationDetailsSource<HttpServletRequest, Any?> { context -> context.getHeader("CUSTOM_HEADER") })
        }
        return bean
    }

    override fun postProcessBeforeInitialization(bean: Any, name: String?): Any {
        return bean
    }
}

次に、この Bean をアプリケーションコンテキストに登録します。Spring は、アプリケーションコンテキストで定義された Bean で自動的に呼び出します。


1FilterInvocation オブジェクトには HttpServletRequest が含まれているため、返された属性のリストに含まれるものに基づいて決定を行うための URL またはその他の関連情報を取得できます。