最新の安定バージョンについては、Spring Security 6.4.2 を使用してください! |
Java 構成
Spring 3.1 の Spring Framework に、Java 構成の一般的なサポートが追加されました。Spring Security 3.2 以降、ユーザーが XML を使用せずに Spring Security を簡単に構成できる Spring Security Java 構成サポートが提供されています。
セキュリティ名前空間の構成に精通している場合は、セキュリティ名前空間の構成と Security Java Configuration サポートの間にかなりの類似点があります。
Spring Security は、Spring Security Java 構成の使用法を示す多くのサンプルアプリケーション [GitHub] (英語) を提供します。 |
Hello Web セキュリティ Java 構成
最初のステップは、Spring Security Java 構成を作成することです。この構成により、アプリケーション内のすべてのセキュリティ(アプリケーション URL の保護、送信されたユーザー名とパスワードの検証、ログインフォームへのリダイレクトなど)を担当する springSecurityFilterChain
と呼ばれるサーブレットフィルターが作成されます。以下に、Spring Security Java 構成の最も基本的な例を示します。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.*;
import org.springframework.security.config.annotation.authentication.builders.*;
import org.springframework.security.config.annotation.web.configuration.*;
@EnableWebSecurity
public class WebSecurityConfig {
@Bean
public UserDetailsService userDetailsService() {
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
manager.createUser(User.withDefaultPasswordEncoder().username("user").password("password").roles("USER").build());
return manager;
}
}
この構成にはそれほど多くはありませんが、多くのことを行います。以下の機能の概要を参照してください。
アプリケーションのすべての URL に認証を要求する
ログインフォームを生成します
ユーザー名 ユーザーとパスワード パスワードを持つユーザーがフォームベース認証で認証できるようにする
ユーザーがログアウトできるようにする
セキュリティヘッダーの統合
セキュアリクエストの HTTP Strict Transport Security [Wikipedia]
キャッシュ制御 (静的リソースのキャッシュを許可するために、アプリケーションによって後でオーバーライドできます)
クリックジャッキング [Wikipedia] の防止に役立つ X-Frame-Options の統合
次のサーブレット API メソッドと統合する
AbstractSecurityWebApplicationInitializer
次のステップは、springSecurityFilterChain
を war に登録することです。これは、Servlet 3.0+ 環境の Spring の WebApplicationInitializer サポートを使用した Java 構成で実行できます。当然のことながら、Spring Security は、springSecurityFilterChain
が確実に登録されるようにする基本クラス AbstractSecurityWebApplicationInitializer
を提供します。AbstractSecurityWebApplicationInitializer
を使用する方法は、すでに Spring を使用しているか、Spring Security がアプリケーションの唯一の Spring コンポーネントであるかによって異なります。
Spring が存在しない AbstractSecurityWebApplicationInitializer - Spring をまだ使用していない場合は、これらの手順を使用してください
Spring MVC を使用した AbstractSecurityWebApplicationInitializer - すでに Spring を使用している場合は、これらの手順を使用してください
Spring が存在しない AbstractSecurityWebApplicationInitializer
Spring または Spring MVC を使用していない場合は、構成が確実に取得されるように、WebSecurityConfig
をスーパークラスに渡す必要があります。以下に例を示します。
import org.springframework.security.web.context.*;
public class SecurityWebApplicationInitializer
extends AbstractSecurityWebApplicationInitializer {
public SecurityWebApplicationInitializer() {
super(WebSecurityConfig.class);
}
}
SecurityWebApplicationInitializer
は次のことを行います。
アプリケーション内のすべての URL に springSecurityFilterChain フィルターを自動的に登録します
WebSecurityConfig をロードする ContextLoaderListener を追加します。
Spring MVC を使用した AbstractSecurityWebApplicationInitializer
アプリケーションの他の場所で Spring を使用していた場合、おそらく Spring 構成を読み込んでいる WebApplicationInitializer
がすでに存在していることでしょう。以前の構成を使用すると、エラーが発生します。代わりに、Spring Security を既存の ApplicationContext
に登録する必要があります。例: Spring MVC を使用している場合、SecurityWebApplicationInitializer
は次のようになります。
import org.springframework.security.web.context.*;
public class SecurityWebApplicationInitializer
extends AbstractSecurityWebApplicationInitializer {
}
これは、アプリケーション内のすべての URL に対して springSecurityFilterChain フィルターを登録するだけです。その後、WebSecurityConfig
が既存の ApplicationInitializer にロードされていることを確認します。例: Spring MVC を使用している場合は、getRootConfigClasses()
に追加されます。
public class MvcWebApplicationInitializer extends
AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[] { WebSecurityConfig.class };
}
// ... other overrides ...
}
HttpSecurity
これまでのところ、WebSecurityConfig には、ユーザーを認証する方法に関する情報のみが含まれています。Spring Security は、すべてのユーザーに認証を要求することをどのように認識しますか? Spring Security は、フォームベース認証をサポートすることをどのように認識していますか? 実際には、SecurityFilterChain
と呼ばれるバックグラウンドで呼び出されている Bean があります。これは、次のデフォルトの実装で構成されています。
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeRequests(authorize -> authorize
.anyRequest().authenticated()
)
.formLogin(withDefaults())
.httpBasic(withDefaults());
return http.build();
}
上記のデフォルト構成:
アプリケーションへのリクエストでは、ユーザーの認証が必要であることを保証します
ユーザーがフォームベースのログインで認証できるようにします
ユーザーが HTTP 基本認証で認証できるようにします
この構成は、XML 名前空間の構成と非常に似ていることがわかります。
<http>
<intercept-url pattern="/**" access="authenticated"/>
<form-login />
<http-basic />
</http>
複数の HttpSecurity
複数の <http>
ブロックを持つことができるのと同じように、複数の HttpSecurity
インスタンスを構成できます。重要なのは、複数の SecurityFilterChain
@Bean
を登録することです。例: 以下は、/api/
で始まる URL の異なる構成の例です。
@EnableWebSecurity
public class MultiHttpSecurityConfig {
@Bean (1)
public UserDetailsService userDetailsService() throws Exception {
// ensure the passwords are encoded properly
UserBuilder users = User.withDefaultPasswordEncoder();
InMemoryUserDetailsManager manager = new 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)
public SecurityFilterChain apiFilterChain(HttpSecurity http) throws Exception {
http
.antMatcher("/api/**") (3)
.authorizeHttpRequests(authorize -> authorize
.anyRequest().hasRole("ADMIN")
)
.httpBasic(withDefaults());
return http.build();
}
@Bean (4)
public SecurityFilterChain formLoginFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(authorize -> authorize
.anyRequest().authenticated()
)
.formLogin(withDefaults());
return http.build();
}
}
1 | 通常の認証を構成する |
2 | @Order を含む SecurityFilterChain のインスタンスを登録して、どの SecurityFilterChain を最初に検討するかを指定します。 |
3 | http.antMatcher は、この HttpSecurity は /api/ で始まる URL にのみ適用可能であると述べています |
4 | SecurityFilterChain の別のインスタンスを登録します。URL が /api/ で始まらない場合は、この構成が使用されます。この構成は、1 の後に @Order 値があるため、apiFilterChain の後に考慮されます(@Order のデフォルトは持続しません)。 |
カスタム DSL
Spring Security で独自のカスタム DSL を提供できます。例: 次のようなものがあります。
public class MyCustomDsl extends AbstractHttpConfigurer<MyCustomDsl, HttpSecurity> {
private boolean flag;
@Override
public void init(HttpSecurity http) throws Exception {
// any method that adds another configurer
// must be done in the init method
http.csrf().disable();
}
@Override
public void configure(HttpSecurity http) throws Exception {
ApplicationContext context = http.getSharedObject(ApplicationContext.class);
// here we lookup from the ApplicationContext. You can also just create a new instance.
MyFilter myFilter = context.getBean(MyFilter.class);
myFilter.setFlag(flag);
http.addFilterBefore(myFilter, UsernamePasswordAuthenticationFilter.class);
}
public MyCustomDsl flag(boolean value) {
this.flag = value;
return this;
}
public static MyCustomDsl customDsl() {
return new MyCustomDsl();
}
}
これは、実際に HttpSecurity.authorizeRequests() などのメソッドが実装される方法です。 |
カスタム DSL は次のように使用できます。
@EnableWebSecurity
public class Config {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.apply(customDsl())
.flag(true)
.and()
...;
return http.build();
}
}
コードは次の順序で呼び出されます。
`Config` の configure メソッドのコードが呼び出されます
`MyCustomDsl` の init メソッドのコードが呼び出されます
`MyCustomDsl` の configure メソッドのコードが呼び出されます
必要に応じて、SpringFactories
を使用して、デフォルトで MyCustomDsl
を HttpSecurity
に追加できます。例: 次の内容で META-INF/spring.factories
という名前のクラスパスにリソースを作成します。
org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer = sample.MyCustomDsl
デフォルトを無効にしたいユーザーは、明示的に無効にすることができます。
@EnableWebSecurity
public class Config {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.apply(customDsl()).disable()
...;
return http.build();
}
}
構成済みオブジェクトの後処理
Spring Security の Java 構成は、構成するすべてのオブジェクトのすべてのプロパティを公開しません。これにより、大多数のユーザーの構成が簡素化されます。結局、すべてのプロパティが公開された場合、ユーザーは標準の Bean 構成を使用できます。
すべてのプロパティを直接公開しない正当な理由はありますが、ユーザーはさらに高度な構成オプションが必要になる場合があります。この Spring Security に対処するために、Java 構成によって作成されたオブジェクトインスタンスの多くを変更または置換するために使用できる ObjectPostProcessor
の概念が導入されています。例: FilterSecurityInterceptor
で filterSecurityPublishAuthorizationSuccess
プロパティを設定する場合、次を使用できます。
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeRequests(authorize -> authorize
.anyRequest().authenticated()
.withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
public <O extends FilterSecurityInterceptor> O postProcess(
O fsi) {
fsi.setPublishAuthorizationSuccess(true);
return fsi;
}
})
);
return http.build();
}