Kotlin の設定
Spring Security Kotlin 構成は、Spring Security 5.3 から利用可能になりました。これにより、ユーザーはネイティブ Kotlin DSL を使用して Spring Security を構成できるようになります。
Spring Security は、Spring Security Kotlin 構成の使用箇所を示すためにサンプルアプリケーション [GitHub] (英語) を提供します。 |
HttpSecurity
Spring Security は、すべてのユーザーに認証を要求することをどのように認識しますか? Spring Security は、フォームベースの認証をサポートすることをどのように認識していますか? バックグラウンドで呼び出されている構成クラス(SecurityFilterChain
と呼ばれる)があります。これは、次のデフォルトの実装で構成されています。
import org.springframework.security.config.annotation.web.invoke
@Bean
open fun filterChain(http: HttpSecurity): SecurityFilterChain {
http {
authorizeHttpRequests {
authorize(anyRequest, authenticated)
}
formLogin { }
httpBasic { }
}
return http.build()
}
IDE がメソッドを常に自動インポートするとは限らず、コンパイルの問題が発生する可能性があるため、クラスで Kotlin DSL を有効にするには、必ず org.springframework.security.config.annotation.web.invoke 関数をインポートしてください。 |
デフォルトの構成(前の例に示されています):
アプリケーションへのリクエストでは、ユーザーの認証が必要であることを保証します
ユーザーがフォームベースのログインで認証できるようにします
ユーザーが HTTP 基本認証で認証できるようにします
この構成は XML 名前空間の構成と類似していることに注意してください。
<http>
<intercept-url pattern="/**" access="authenticated"/>
<form-login />
<http-basic />
</http>
複数の HttpSecurity インスタンス
特定の領域に異なる保護が必要なアプリケーションのセキュリティを効果的に管理するには、securityMatcher
DSL メソッドと並行して複数のフィルターチェーンを使用します。このアプローチにより、アプリケーションの特定の部分に合わせて個別のセキュリティ構成を定義し、アプリケーション全体のセキュリティと制御を強化できます。
XML で複数の <http>
ブロックを持つことができるのと同じように、複数の HttpSecurity
インスタンスを構成できます。重要なのは、複数の SecurityFilterChain
または @Bean
を登録することです。次の例では、/api/
で始まる URL に対して異なる構成が使用されています。
import org.springframework.security.config.annotation.web.invoke
@Configuration
@EnableWebSecurity
class MultiHttpSecurityConfig {
@Bean (1)
open fun userDetailsService(): UserDetailsService {
val users = User.withDefaultPasswordEncoder()
val manager = InMemoryUserDetailsManager()
manager.createUser(users.username("user").password("password").roles("USER").build())
manager.createUser(users.username("admin").password("password").roles("USER","ADMIN").build())
return manager
}
@Bean
@Order(1) (2)
open fun apiFilterChain(http: HttpSecurity): SecurityFilterChain {
http {
securityMatcher("/api/**") (3)
authorizeHttpRequests {
authorize(anyRequest, hasRole("ADMIN"))
}
httpBasic { }
}
return http.build()
}
@Bean (4)
open fun formLoginFilterChain(http: HttpSecurity): SecurityFilterChain {
http {
authorizeHttpRequests {
authorize(anyRequest, authenticated)
}
formLogin { }
}
return http.build()
}
}
1 | 通常どおり認証を構成します。 |
2 | @Order を含む SecurityFilterChain のインスタンスを作成して、どの SecurityFilterChain を最初に考慮するかを指定します。 |
3 | http.securityMatcher() では、この HttpSecurity は /api/ で始まる URL にのみ適用されることが規定されています。 |
4 | SecurityFilterChain の別のインスタンスを作成します。URL が /api/ で始まらない場合は、この構成が使用されます。この構成は、1 の後に @Order 値があるため、apiFilterChain の後に考慮されます (@Order はデフォルトで最後にはなりません)。 |
securityMatcher
または requestMatchers
の選択
よくある質問は次のとおりです。
リクエストの認可に使用される
http.securityMatcher()
メソッドとrequestMatchers()
(つまり、http.authorizeHttpRequests()
内部) の違いは何ですか ?
この質問に答えるには、SecurityFilterChain
の構築に使用される各 HttpSecurity
インスタンスに、受信リクエストに一致する RequestMatcher
が含まれていることを理解すると役立ちます。リクエストが優先度の高い SecurityFilterChain
(例: @Order(1)
) と一致しない場合、リクエストは優先度の低いフィルターチェーン (例: @Order
なし) に対して試行されます。
複数のフィルターチェーンのマッチングロジックは、 |
デフォルトの RequestMatcher
はすべてのリクエストに一致し、Spring Security がデフォルトですべてのリクエストを保護するようにします。
|
特定のリクエストに一致するフィルターチェーンがない場合、そのリクエストは Spring Security によって保護されません。 |
次の例は、/secured/
で始まるリクエストのみを保護する単一のフィルターチェーンを示しています。
import org.springframework.security.config.annotation.web.invoke
@Configuration
@EnableWebSecurity
class PartialSecurityConfig {
@Bean
open fun userDetailsService(): UserDetailsService {
// ...
}
@Bean
open fun securedFilterChain(http: HttpSecurity): SecurityFilterChain {
http {
securityMatcher("/secured/**") (1)
authorizeHttpRequests {
authorize("/secured/user", hasRole("USER")) (2)
authorize("/secured/admin", hasRole("ADMIN")) (3)
authorize(anyRequest, authenticated) (4)
}
httpBasic { }
formLogin { }
}
return http.build()
}
}
1 | /secured/ で始まるリクエストは保護されますが、その他のリクエストは保護されません。 |
2 | /secured/user へのリクエストには ROLE_USER 権限が必要です。 |
3 | /secured/admin へのリクエストには ROLE_ADMIN 権限が必要です。 |
4 | その他のリクエスト (/secured/other など) では、認証されたユーザーのみが必要です。 |
前の例で示したように、アプリケーション全体が保護されるように、 |
requestMatchers
メソッドは個々の認可ルールにのみ適用されることに注意してください。そこにリストされている各リクエストは、SecurityFilterChain
の作成に使用されたこの特定の HttpSecurity
インスタンスの全体的な securityMatcher
とも一致する必要があります。この例で anyRequest()
を使用すると、この特定の SecurityFilterChain
( /secured/
で始まる必要があります) 内の他のすべてのリクエストと一致します。
|
SecurityFilterChain
エンドポイント
SecurityFilterChain
のいくつかのフィルターは、http.formLogin()
によってセットアップされ、POST /login
エンドポイントを提供する UsernamePasswordAuthenticationFilter
など、エンドポイントを直接提供します。上記の例では、/login
エンドポイントは http.securityMatcher("/secured/**")
と一致しないため、そのアプリケーションには GET /login
または POST /login
エンドポイントがありません。このようなリクエストは 404 Not Found
を返します。これはユーザーにとってしばしば驚きです。
http.securityMatcher()
を指定すると、その SecurityFilterChain
に一致するリクエストに影響します。ただし、フィルターチェーンによって提供されるエンドポイントには自動的に影響しません。このような場合は、フィルターチェーンが提供するエンドポイントの URL をカスタマイズする必要がある場合があります。
次の例は、/secured/
で始まるリクエストを保護し、他のすべてのリクエストを拒否するとともに、SecurityFilterChain
によって提供されるエンドポイントをカスタマイズする構成を示しています。
import org.springframework.security.config.annotation.web.invoke
@Configuration
@EnableWebSecurity
class SecuredSecurityConfig {
@Bean
open fun userDetailsService(): UserDetailsService {
// ...
}
@Bean
@Order(1)
open fun securedFilterChain(http: HttpSecurity): SecurityFilterChain {
http {
securityMatcher("/secured/**") (1)
authorizeHttpRequests {
authorize(anyRequest, authenticated) (2)
}
formLogin { (3)
loginPage = "/secured/login"
loginProcessingUrl = "/secured/login"
permitAll = true
}
logout { (4)
logoutUrl = "/secured/logout"
logoutSuccessUrl = "/secured/login?logout"
permitAll = true
}
}
return http.build()
}
@Bean
open fun defaultFilterChain(http: HttpSecurity): SecurityFilterChain {
http {
authorizeHttpRequests {
authorize(anyRequest, denyAll) (5)
}
}
return http.build()
}
}
1 | /secured/ で始まるリクエストは、このフィルターチェーンによって保護されます。 |
2 | /secured/ で始まるリクエストには認証されたユーザーが必要です。 |
3 | フォームログインをカスタマイズして、URL の先頭に /secured/ を付けます。 |
4 | ログアウトをカスタマイズして、URL のプレフィックスに /secured/ を付けます。 |
5 | その他のリクエストはすべて拒否されます。 |
この例では、ログインページとログアウトページをカスタマイズし、Spring Security の生成されたページを無効にします。 |
実世界の例
次の例は、これらすべての要素を組み合わせた、もう少し現実的な構成を示しています。
import org.springframework.security.config.annotation.web.invoke
@Configuration
@EnableWebSecurity
class BankingSecurityConfig {
@Bean (1)
open fun userDetailsService(): UserDetailsService {
val users = User.withDefaultPasswordEncoder()
val manager = InMemoryUserDetailsManager()
manager.createUser(users.username("user1").password("password").roles("USER", "VIEW_BALANCE").build())
manager.createUser(users.username("user2").password("password").roles("USER").build())
manager.createUser(users.username("admin").password("password").roles("ADMIN").build())
return manager
}
@Bean
@Order(1) (2)
open fun approvalsSecurityFilterChain(http: HttpSecurity): SecurityFilterChain {
val approvalsPaths = arrayOf("/accounts/approvals/**", "/loans/approvals/**", "/credit-cards/approvals/**")
http {
securityMatcher(approvalsPaths)
authorizeHttpRequests {
authorize(anyRequest, hasRole("ADMIN"))
}
httpBasic { }
}
return http.build()
}
@Bean
@Order(2) (3)
open fun bankingSecurityFilterChain(http: HttpSecurity): SecurityFilterChain {
val bankingPaths = arrayOf("/accounts/**", "/loans/**", "/credit-cards/**", "/balances/**")
val viewBalancePaths = arrayOf("/balances/**")
http {
securityMatcher(bankingPaths)
authorizeHttpRequests {
authorize(viewBalancePaths, hasRole("VIEW_BALANCE"))
authorize(anyRequest, hasRole("USER"))
}
}
return http.build()
}
@Bean (4)
open fun defaultSecurityFilterChain(http: HttpSecurity): SecurityFilterChain {
val allowedPaths = arrayOf("/", "/user-login", "/user-logout", "/notices", "/contact", "/register")
http {
authorizeHttpRequests {
authorize(allowedPaths, permitAll)
authorize(anyRequest, authenticated)
}
formLogin {
loginPage = "/user-login"
loginProcessingUrl = "/user-login"
}
logout {
logoutUrl = "/user-logout"
logoutSuccessUrl = "/?logout"
}
}
return http.build()
}
}
1 | まず認証設定を構成します。 |
2 | @Order(1) を使用して SecurityFilterChain インスタンスを定義します。つまり、このフィルターチェーンの優先度が最も高くなります。このフィルターチェーンは、/accounts/approvals/ 、/loans/approvals/ 、または /credit-cards/approvals/ で始まるリクエストにのみ適用されます。このフィルターチェーンへのリクエストには ROLE_ADMIN 権限が必要であり、HTTP 基本認証が許可されます。 |
3 | 次に、2 番目とみなされる @Order(2) を使用して、別の SecurityFilterChain インスタンスを作成します。このフィルターチェーンは、/accounts/ 、/loans/ 、/credit-cards/ または /balances/ で始まるリクエストにのみ適用されます。このフィルターチェーンは 2 番目であるため、/approvals/ を含むすべてのリクエストは前のフィルターチェーンと一致し、このフィルターチェーンとは一致しないことに注意してください。このフィルターチェーンへのリクエストには、ROLE_USER 権限が必要です。このフィルターチェーンは、次の (デフォルトの) フィルターチェーンにその構成が含まれているため、認証を定義しません。 |
4 | 最後に、@Order アノテーションなしで追加の SecurityFilterChain インスタンスを作成します。この構成は、他のフィルターチェーンでカバーされていないリクエストを処理し、最後に処理されます (@Order がない場合は最後にデフォルトで処理されます)。/ 、/user-login 、/user-logout 、/notices 、/contact 、/register に一致するリクエストは、認証なしでアクセスできます。その他のリクエストでは、他のフィルターチェーンによって明示的に許可または保護されていない URL にアクセスするには、ユーザーの認証が必要です。 |