最新の安定バージョンについては、Spring Security 6.4.1 を使用してください! |
セッション管理
先行したセッションの作成を強制する
時には、先行してセッションを作成することが価値がある場合があります。これは、次を使用して構成できる ForceEagerSessionCreationFilter
(Javadoc) を使用して実行できます。
Java
XML
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) {
http
.sessionManagement(session -> session
.sessionCreationPolicy(SessionCreationPolicy.ALWAYS)
);
return http.build();
}
<http create-session="ALWAYS">
</http>
タイムアウトの検出
Spring Security を構成して、無効なセッション ID の送信を検出し、ユーザーを適切な URL にリダイレクトできます。これは、session-management
要素によって実現されます。
Java
XML
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) {
http
.sessionManagement(session -> session
.invalidSessionUrl("/invalidSession.htm")
);
return http.build();
}
<http>
...
<session-management invalid-session-url="/invalidSession.htm" />
</http>
このメカニズムを使用してセッションタイムアウトを検出した場合、ユーザーがログアウトしてからブラウザーを閉じずに再度ログインすると、誤ってエラーが報告される可能性があることに注意してください。これは、セッションを無効にしてもセッション Cookie がクリアされず、ユーザーがログアウトしても再送信されるためです。ログアウトハンドラーで次の構文を使用するなどして、ログアウト時に JSESSIONID Cookie を明示的に削除できる場合があります。
Java
XML
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) {
http
.logout(logout -> logout
.deleteCookies("JSESSIONID")
);
return http.build();
}
<http>
<logout delete-cookies="JSESSIONID" />
</http>
残念ながら、これはすべてのサーブレットコンテナーで動作することを保証できないため、自分の環境でテストする必要があります。
アプリケーションをプロキシの背後で実行している場合は、プロキシサーバーを構成することでセッションクッキーを削除できる場合もあります。例: Apache HTTPD の mod_headers を使用すると、次のディレクティブは、ログアウトリクエストへのレスポンスで期限切れにすることで
|
同時セッション制御
1 人のユーザーがアプリケーションにログインする機能に制約を課したい場合、Spring Security は、次の簡単な追加で、これをすぐにサポートします。まず、セッションライフサイクルイベントについて Spring Security を最新の状態に保つために、構成に次のリスナーを追加する必要があります。
Java
XML
@Bean
public HttpSessionEventPublisher httpSessionEventPublisher() {
return new HttpSessionEventPublisher();
}
<listener>
<listener-class>
org.springframework.security.web.session.HttpSessionEventPublisher
</listener-class>
</listener>
次に、アプリケーションコンテキストに次の行を追加します。
Java
XML
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) {
http
.sessionManagement(session -> session
.maximumSessions(1)
);
return http.build();
}
<http>
...
<session-management>
<concurrency-control max-sessions="1" />
</session-management>
</http>
これにより、ユーザーが複数回ログインするのを防ぐことができます。2 回目のログインでは、最初のログインが無効になります。多くの場合、2 回目のログインを禁止することをお勧めします。その場合
Java
XML
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) {
http
.sessionManagement(session -> session
.maximumSessions(1)
.maxSessionsPreventsLogin(true)
);
return http.build();
}
<http>
<session-management>
<concurrency-control max-sessions="1" error-if-maximum-exceeded="true" />
</session-management>
</http>
2 回目のログインは拒否されます。「拒否」とは、フォームベースのログインが使用されている場合、ユーザーが authentication-failure-url
に送信されることを意味します。2 番目の認証が "remember-me" などの別の非対話型メカニズムを介して行われる場合、"unauthorized" (401) エラーがクライアントに送信されます。代わりにエラーページを使用する場合は、session-authentication-error-url
属性を session-management
要素に追加できます。
フォームベースのログインにカスタマイズされた認証フィルターを使用している場合、同時セッション制御サポートを明示的に構成する必要があります。詳細については、セッション管理の章を参照してください。
セッション固定攻撃保護
セッション固定 [Wikipedia] 攻撃は、悪意のある攻撃者がサイトにアクセスしてセッションを作成し、別のユーザーに同じセッションでログインするように誘導することができる潜在的なリスクです(たとえば、パラメーターとしてセッション識別子を含むリンクを送信することなど)。Spring Security は、ユーザーがログインしたときに新しいセッションを作成するか、セッション ID を変更することで、自動的にこの問題から保護します。この保護を必要としない場合、または他の要件と競合する場合は、<session-management>
の session-fixation-protection
属性を使用して動作を制御することができます(4 つのオプションがあります)。
none
- 何もしないでください。元のセッションは保持されます。newSession
- 既存のセッションデータをコピーせずに、新しい「クリーン」セッションを作成します(Spring セキュリティ関連の属性は引き続きコピーされます)。migrateSession
- 新しいセッションを作成し、既存のすべてのセッション属性を新しいセッションにコピーします。これは、Servlet 3.0 以前のコンテナーのデフォルトです。changeSessionId
- 新しいセッションを作成しません。代わりに、サーブレットコンテナー(HttpServletRequest#changeSessionId()
)によって提供されるセッション固定保護を使用します。このオプションは、Servlet 3.1(Java EE 7)以降のコンテナーでのみ使用可能です。古いコンテナーで指定すると、例外が発生します。これは、Servlet 3.1 以降のコンテナーのデフォルトです。
セッション固定保護が発生すると、SessionFixationProtectionEvent
がアプリケーションコンテキストで公開されます。changeSessionId
を使用する場合、この保護により javax.servlet.http.HttpSessionIdListener
も通知されるため、コードが両方のイベントをリッスンする場合は注意が必要です。詳細については、セッション管理の章を参照してください。
SessionManagementFilter
SessionManagementFilter
は、SecurityContextRepository
のコンテンツを SecurityContextHolder
の現在のコンテンツと照合して、通常、事前認証や remember-me [ 1 ] などの非対話型認証メカニズムによって現在のリクエスト中にユーザーが認証されたかどうかを判断します。リポジトリにセキュリティコンテキストが含まれている場合、フィルターは何もしません。存在せず、スレッドローカル SecurityContext
に(非匿名の) Authentication
オブジェクトが含まれている場合、フィルターは、スタック内の前のフィルターによって認証されたと見なします。次に、構成された SessionAuthenticationStrategy
を呼び出します。
ユーザーが現在認証されていない場合、フィルターは無効なセッション ID がリクエストされているかどうかを確認し(タイムアウトなどが原因)、設定されている InvalidSessionStrategy
が設定されている場合はそれを呼び出します。最も一般的な動作は、固定 URL にリダイレクトすることであり、これは標準実装 SimpleRedirectInvalidSessionStrategy
にカプセル化されています。後者は、前述のように、名前空間を介して無効なセッション URL を構成するときにも使用されます。
SessionAuthenticationStrategy
SessionAuthenticationStrategy
は SessionManagementFilter
と AbstractAuthenticationProcessingFilter
の両方で使用されるため、たとえば、カスタマイズされたフォームログインクラスを使用している場合は、これらの両方にそれを挿入する必要があります。この場合、名前空間とカスタム Bean を組み合わせた一般的な構成は次のようになります。
<http>
<custom-filter position="FORM_LOGIN_FILTER" ref="myAuthFilter" />
<session-management session-authentication-strategy-ref="sas"/>
</http>
<beans:bean id="myAuthFilter" class=
"org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter">
<beans:property name="sessionAuthenticationStrategy" ref="sas" />
...
</beans:bean>
<beans:bean id="sas" class=
"org.springframework.security.web.authentication.session.SessionFixationProtectionStrategy" />
Spring セッションスコープ Bean を含む HttpSessionBindingListener
を実装するセッションに Bean を格納している場合、デフォルトの SessionFixationProtectionStrategy
を使用すると問題が発生する可能性があることに注意してください。詳細については、このクラスの Javadoc を参照してください。
並行性制御
Spring Security は、プリンシパルが指定された回数を超えて同じアプリケーションに対して同時に認証するのを防ぐことができます。多くの ISV はこれを利用してライセンスを強制しますが、ネットワーク管理者がこの機能を好むのは、ユーザーがログイン名を共有できないようにするためです。たとえば、ユーザー "Batman" が 2 つの異なるセッションから Web アプリケーションにログオンするのを停止できます。以前のログインを期限切れにするか、再度ログインしようとしたときにエラーを報告して、2 回目のログインを防ぐことができます。2 番目の方法を使用している場合、明示的にログアウトしていないユーザー(たとえば、ブラウザーを閉じたばかりのユーザー)は、元のセッションが期限切れになるまで再度ログインできないことに注意してください。
同時実行制御はネームスペースでサポートされているため、最も単純な構成については、以前のネームスペースの章を確認してください。ただし、場合によってはカスタマイズする必要があります。
実装では、ConcurrentSessionControlAuthenticationStrategy
と呼ばれる SessionAuthenticationStrategy
の特殊バージョンを使用します。
以前は、同時認証チェックは |
同時セッションのサポートを使用するには、以下を web.xml
に追加する必要があります。
<listener>
<listener-class>
org.springframework.security.web.session.HttpSessionEventPublisher
</listener-class>
</listener>
さらに、ConcurrentSessionFilter
を FilterChainProxy
に追加する必要があります。ConcurrentSessionFilter
には、2 つのコンストラクター引数 sessionRegistry
が必要です。これは一般に SessionRegistryImpl
のインスタンスを指し、sessionInformationExpiredStrategy
はセッションの有効期限が切れたときに適用する戦略を定義します。ネームスペースを使用して FilterChainProxy
およびその他のデフォルト Bean を作成する構成は、次のようになります。
<http>
<custom-filter position="CONCURRENT_SESSION_FILTER" ref="concurrencyFilter" />
<custom-filter position="FORM_LOGIN_FILTER" ref="myAuthFilter" />
<session-management session-authentication-strategy-ref="sas"/>
</http>
<beans:bean id="redirectSessionInformationExpiredStrategy"
class="org.springframework.security.web.session.SimpleRedirectSessionInformationExpiredStrategy">
<beans:constructor-arg name="invalidSessionUrl" value="/session-expired.htm" />
</beans:bean>
<beans:bean id="concurrencyFilter"
class="org.springframework.security.web.session.ConcurrentSessionFilter">
<beans:constructor-arg name="sessionRegistry" ref="sessionRegistry" />
<beans:constructor-arg name="sessionInformationExpiredStrategy" ref="redirectSessionInformationExpiredStrategy" />
</beans:bean>
<beans:bean id="myAuthFilter" class=
"org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter">
<beans:property name="sessionAuthenticationStrategy" ref="sas" />
<beans:property name="authenticationManager" ref="authenticationManager" />
</beans:bean>
<beans:bean id="sas" class="org.springframework.security.web.authentication.session.CompositeSessionAuthenticationStrategy">
<beans:constructor-arg>
<beans:list>
<beans:bean class="org.springframework.security.web.authentication.session.ConcurrentSessionControlAuthenticationStrategy">
<beans:constructor-arg ref="sessionRegistry"/>
<beans:property name="maximumSessions" value="1" />
<beans:property name="exceptionIfMaximumExceeded" value="true" />
</beans:bean>
<beans:bean class="org.springframework.security.web.authentication.session.SessionFixationProtectionStrategy">
</beans:bean>
<beans:bean class="org.springframework.security.web.authentication.session.RegisterSessionAuthenticationStrategy">
<beans:constructor-arg ref="sessionRegistry"/>
</beans:bean>
</beans:list>
</beans:constructor-arg>
</beans:bean>
<beans:bean id="sessionRegistry"
class="org.springframework.security.core.session.SessionRegistryImpl" />
web.xml
にリスナーを追加すると、HttpSession
が開始または終了するたびに ApplicationEvent
が Spring ApplicationContext
に公開されます。これは、セッションが終了したときに SessionRegistryImpl
に通知されるため、重要です。これがないと、ユーザーが別のセッションからログアウトしたりタイムアウトしたりした場合でも、セッションの許容量を超えると、ユーザーは再びログインできなくなります。
現在認証されているユーザーとそのセッションについて SessionRegistry を照会する
名前空間またはプレーン Bean を使用して同時実行制御を設定すると、アプリケーション内で直接使用できる SessionRegistry
への参照が提供されるという便利な副作用があります。ユーザーが持つ可能性のあるセッションについては、インフラストラクチャをセットアップする価値があるかもしれません。maximumSession
プロパティを -1 に設定して、無制限のセッションを許可できます。名前空間を使用している場合は、session-registry-alias
属性を使用して内部で作成された SessionRegistry
のエイリアスを設定し、独自の Bean に注入できる参照を提供できます。
getAllPrincipals()
メソッドは、現在認証されているユーザーのリストを提供します。ユーザーのセッションをリストするには、getAllSessions(Object principal, boolean includeExpiredSessions)
メソッドを呼び出して、SessionInformation
オブジェクトのリストを返します。SessionInformation
インスタンスで expireNow()
を呼び出すことにより、ユーザーのセッションを期限切れにすることもできます。ユーザーがアプリケーションに戻ると、ユーザーは続行できなくなります。たとえば、これらのメソッドは管理アプリケーションで役立ちます。詳細については、Javadoc を参照してください。
SessionManagementFilter
によって検出されません。これらの場合、セッション管理機能を個別に処理する必要があります。