最新の安定バージョンについては、Spring Security 6.4.2 を使用してください! |
同時セッションの制御
サーブレットの同時セッション制御と同様に、Spring Security も、ユーザーが Reactive アプリケーションで保持できる同時セッションの数を制限するサポートを提供します。
Spring Security で同時セッション制御を設定すると、フォームログイン、OAuth 2.0 ログイン、HTTP 基本認証を通じて実行される認証を、これらの認証メカニズムが認証の成功を処理する方法にフックすることによって監視します。具体的には、セッション管理 DSL は、認証フィルターで使用される ServerAuthenticationSuccessHandler
のリストに ConcurrentSessionControlServerAuthenticationSuccessHandler (Javadoc) と RegisterSessionServerAuthenticationSuccessHandler (Javadoc) を追加します。
次のセクションには、同時セッション制御を構成する方法の例が含まれています。
同時セッションの制限
デフォルトでは、Spring Security はユーザーに対して任意の数の同時セッションを許可します。同時セッションの数を制限するには、maximumSessions
DSL メソッドを使用できます。
Java
Kotlin
@Bean
SecurityWebFilterChain filterChain(ServerHttpSecurity http) {
http
// ...
.sessionManagement((sessions) -> sessions
.concurrentSessions((concurrency) -> concurrency
.maximumSessions(SessionLimit.of(1))
)
);
return http.build();
}
@Bean
ReactiveSessionRegistry reactiveSessionRegistry() {
return new InMemoryReactiveSessionRegistry();
}
@Bean
open fun springSecurity(http: ServerHttpSecurity): SecurityWebFilterChain {
return http {
// ...
sessionManagement {
sessionConcurrency {
maximumSessions = SessionLimit.of(1)
}
}
}
}
@Bean
open fun reactiveSessionRegistry(): ReactiveSessionRegistry {
return InMemoryReactiveSessionRegistry()
}
上記の構成では、どのユーザーに対しても 1 つのセッションが許可されます。同様に、SessionLimit#UNLIMITED
定数を使用して無制限のセッションを許可することもできます。
Java
Kotlin
@Bean
SecurityWebFilterChain filterChain(ServerHttpSecurity http) {
http
// ...
.sessionManagement((sessions) -> sessions
.concurrentSessions((concurrency) -> concurrency
.maximumSessions(SessionLimit.UNLIMITED))
);
return http.build();
}
@Bean
ReactiveSessionRegistry reactiveSessionRegistry() {
return new InMemoryReactiveSessionRegistry();
}
@Bean
open fun springSecurity(http: ServerHttpSecurity): SecurityWebFilterChain {
return http {
// ...
sessionManagement {
sessionConcurrency {
maximumSessions = SessionLimit.UNLIMITED
}
}
}
}
@Bean
open fun reactiveSessionRegistry(webSessionManager: WebSessionManager): ReactiveSessionRegistry {
return InMemoryReactiveSessionRegistry()
}
maximumSessions
メソッドは SessionLimit
インターフェースを受け入れ、これにより Function<Authentication, Mono<Integer>>
が拡張されるため、ユーザーの認証に基づいてセッションの最大数を決定するためのより複雑なロジックを使用できます。
Authentication
に基づいて maximumSessions を構成する Java
Kotlin
@Bean
SecurityWebFilterChain filterChain(ServerHttpSecurity http) {
http
// ...
.sessionManagement((sessions) -> sessions
.concurrentSessions((concurrency) -> concurrency
.maximumSessions(maxSessions()))
);
return http.build();
}
private SessionLimit maxSessions() {
return (authentication) -> {
if (authentication.getAuthorities().contains(new SimpleGrantedAuthority("ROLE_UNLIMITED_SESSIONS"))) {
return Mono.empty(); // allow unlimited sessions for users with ROLE_UNLIMITED_SESSIONS
}
if (authentication.getAuthorities().contains(new SimpleGrantedAuthority("ROLE_ADMIN"))) {
return Mono.just(2); // allow two sessions for admins
}
return Mono.just(1); // allow one session for every other user
};
}
@Bean
ReactiveSessionRegistry reactiveSessionRegistry() {
return new InMemoryReactiveSessionRegistry();
}
@Bean
open fun springSecurity(http: ServerHttpSecurity): SecurityWebFilterChain {
return http {
// ...
sessionManagement {
sessionConcurrency {
maximumSessions = maxSessions()
}
}
}
}
fun maxSessions(): SessionLimit {
return { authentication ->
if (authentication.authorities.contains(SimpleGrantedAuthority("ROLE_UNLIMITED_SESSIONS"))) Mono.empty
if (authentication.authorities.contains(SimpleGrantedAuthority("ROLE_ADMIN"))) Mono.just(2)
Mono.just(1)
}
}
@Bean
open fun reactiveSessionRegistry(): ReactiveSessionRegistry {
return InMemoryReactiveSessionRegistry()
}
セッションの最大数を超えると、デフォルトでは、最も最近使用されていないセッションが期限切れになります。この動作を変更する場合は、セッションの最大数を超えたときに使用する戦略をカスタマイズできます。
同時セッション管理では、たとえば OAuth 2 ログイン経由で使用できる別のセッションがアイデンティティプロバイダーに存在するかどうかは認識されません。アイデンティティプロバイダーに対してセッションを無効にする必要もある場合は、独自の |
最大セッション数を超えた場合の処理
デフォルトでは、セッションの最大数を超えると、最も最近使用されていないセッションが InvalidateLeastUsedMaximumSessionsExceededHandler (Javadoc) を使用して期限切れになります。Spring Security は、ユーザーが PreventLoginMaximumSessionsExceededHandler (Javadoc) を使用して新しいセッションを作成できないようにする別の実装も提供します。独自の戦略を使用したい場合は、ServerMaximumSessionsExceededHandler (Javadoc) の別の実装を提供できます。
Java
Kotlin
@Bean
SecurityWebFilterChain filterChain(ServerHttpSecurity http) {
http
// ...
.sessionManagement((sessions) -> sessions
.concurrentSessions((concurrency) -> concurrency
.maximumSessions(SessionLimit.of(1))
.maximumSessionsExceededHandler(new PreventLoginMaximumSessionsExceededHandler())
)
);
return http.build();
}
@Bean
ReactiveSessionRegistry reactiveSessionRegistry() {
return new InMemoryReactiveSessionRegistry();
}
@Bean
open fun springSecurity(http: ServerHttpSecurity): SecurityWebFilterChain {
return http {
// ...
sessionManagement {
sessionConcurrency {
maximumSessions = SessionLimit.of(1)
maximumSessionsExceededHandler = PreventLoginMaximumSessionsExceededHandler()
}
}
}
}
@Bean
open fun reactiveSessionRegistry(): ReactiveSessionRegistry {
return InMemoryReactiveSessionRegistry()
}
ReactiveSessionRegistry
の指定
Spring Security はユーザーのセッションを追跡するために ReactiveSessionRegistry (Javadoc) を使用し、ユーザーがログインするたびにセッション情報が保存されます。
Spring Security には、ReactiveSessionRegistry
の InMemoryReactiveSessionRegistry (Javadoc) 実装が付属しています。
ReactiveSessionRegistry
実装を指定するには、それを Bean として宣言します。
Java
Kotlin
@Bean
SecurityWebFilterChain filterChain(ServerHttpSecurity http) {
http
// ...
.sessionManagement((sessions) -> sessions
.concurrentSessions((concurrency) -> concurrency
.maximumSessions(SessionLimit.of(1))
)
);
return http.build();
}
@Bean
ReactiveSessionRegistry reactiveSessionRegistry() {
return new MyReactiveSessionRegistry();
}
@Bean
open fun springSecurity(http: ServerHttpSecurity): SecurityWebFilterChain {
return http {
// ...
sessionManagement {
sessionConcurrency {
maximumSessions = SessionLimit.of(1)
}
}
}
}
@Bean
open fun reactiveSessionRegistry(): ReactiveSessionRegistry {
return MyReactiveSessionRegistry()
}
または、sessionRegistry
DSL メソッドを使用することもできます。
Java
Kotlin
@Bean
SecurityWebFilterChain filterChain(ServerHttpSecurity http) {
http
// ...
.sessionManagement((sessions) -> sessions
.concurrentSessions((concurrency) -> concurrency
.maximumSessions(SessionLimit.of(1))
.sessionRegistry(new MyReactiveSessionRegistry())
)
);
return http.build();
}
@Bean
open fun springSecurity(http: ServerHttpSecurity): SecurityWebFilterChain {
return http {
// ...
sessionManagement {
sessionConcurrency {
maximumSessions = SessionLimit.of(1)
sessionRegistry = MyReactiveSessionRegistry()
}
}
}
}
登録ユーザーのセッションを無効にする
場合によっては、ユーザーのセッションのすべてまたは一部を無効にできると便利です。たとえば、ユーザーがパスワードを変更したときに、すべてのセッションを無効にして、再度ログインするように強制したい場合があります。これを行うには、ReactiveSessionRegistry
Bean を使用して、すべてのユーザーのセッションを取得し、無効にして、WebSessionStore
から削除します。
Java
public class SessionControl {
private final ReactiveSessionRegistry reactiveSessionRegistry;
private final WebSessionStore webSessionStore;
public Mono<Void> invalidateSessions(String username) {
return this.reactiveSessionRegistry.getAllSessions(username)
.flatMap((session) -> session.invalidate().thenReturn(session))
.flatMap((session) -> this.webSessionStore.removeSession(session.getSessionId()))
.then();
}
}
一部の認証フィルターを無効にする
デフォルトでは、フォームログイン、OAuth 2.0 ログイン、および HTTP 基本認証では、ServerAuthenticationSuccessHandler
が指定されていない限り、同時セッション制御が自動的に構成されます。例: 次の構成では、フォームログインの同時セッション制御が無効になります。
Java
Kotlin
@Bean
SecurityWebFilterChain filterChain(ServerHttpSecurity http) {
http
// ...
.formLogin((login) -> login
.authenticationSuccessHandler(new RedirectServerAuthenticationSuccessHandler("/"))
)
.sessionManagement((sessions) -> sessions
.concurrentSessions((concurrency) -> concurrency
.maximumSessions(SessionLimit.of(1))
)
);
return http.build();
}
@Bean
open fun springSecurity(http: ServerHttpSecurity): SecurityWebFilterChain {
return http {
// ...
formLogin {
authenticationSuccessHandler = RedirectServerAuthenticationSuccessHandler("/")
}
sessionManagement {
sessionConcurrency {
maximumSessions = SessionLimit.of(1)
}
}
}
}
同時セッション制御を無効にせずに成功ハンドラーを追加する
同時セッション制御を無効にせずに、認証フィルターで使用されるハンドラーのリストに追加の ServerAuthenticationSuccessHandler
インスタンスを含めることもできます。これを行うには、authenticationSuccessHandler(Consumer<List<ServerAuthenticationSuccessHandler>>)
メソッドを使用します。
Java
@Bean
SecurityWebFilterChain filterChain(ServerHttpSecurity http) {
http
// ...
.formLogin((login) -> login
.authenticationSuccessHandler((handlers) -> handlers.add(new MyAuthenticationSuccessHandler()))
)
.sessionManagement((sessions) -> sessions
.concurrentSessions((concurrency) -> concurrency
.maximumSessions(SessionLimit.of(1))
)
);
return http.build();
}
サンプルアプリケーションの確認
サンプルアプリケーションはここ [GitHub] (英語) で確認できます。