Spring Securityの複数のエントリポイント
1. 概要
このクイックチュートリアルでは、define multiple entry points in a Spring Security applicationを実行する方法を見ていきます。
これは主に、WebSecurityConfigurerAdapterクラスを複数回拡張することにより、XML構成ファイルまたは複数のHttpSecurityインスタンスで複数のhttpブロックを定義することを伴います。
2. Mavenの依存関係
開発には、次の依存関係が必要です。
org.springframework.boot
spring-boot-starter-security
2.0.0.RELEASE
org.springframework.boot
spring-boot-starter-web
2.0.0.RELEASE
org.springframework.boot
spring-boot-starter-thymeleaf
2.0.0.RELEASE
org.springframework.boot
spring-boot-starter-test
2.0.0.RELEASE
org.springframework.security
spring-security-test
5.0.4.RELEASE
spring-boot-starter-security、spring-boot-starter-web、spring-boot-starter-thymeleaf、spring-boot-starter-test、spring-security-testの最新バージョンは、MavenCentralからダウンロードできます。
3. 複数のエントリポイント
3.1. 複数のHTTP要素を持つ複数のエントリポイント
ユーザーソースを保持するメインの構成クラスを定義しましょう。
@Configuration
@EnableWebSecurity
public class MultipleEntryPointsSecurityConfig {
@Bean
public UserDetailsService userDetailsService() throws Exception {
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
manager.createUser(User
.withUsername("user")
.password(encoder().encode("userPass"))
.roles("USER").build());
manager.createUser(User
.withUsername("admin")
.password(encoder().encode("adminPass"))
.roles("ADMIN").build());
return manager;
}
@Bean
public PasswordEncoder encoder() {
return new BCryptPasswordEncoder();
}
}
それでは、セキュリティ設定のhow we can define multiple entry pointsを見てみましょう。
ここでは、基本認証によって駆動される例を使用し、構成でSpring Security supports the definition of multiple HTTP elementsが使用されているという事実をうまく利用します。
Java構成を使用する場合、複数のセキュリティレルムを定義する方法は、WebSecurityConfigurerAdapter基本クラスを拡張する複数の@Configurationクラスを用意することです。それぞれに独自のセキュリティ構成があります。 これらのクラスは静的で、メイン構成内に配置できます。
1つのアプリケーションに複数のエントリポイントを設定する主な目的は、アプリケーションのさまざまな部分にアクセスできるさまざまなタイプのユーザーがいる場合です。
それぞれが異なるアクセス許可と認証モードを持つ3つのエントリポイントで構成を定義しましょう。
-
HTTP基本認証を使用する管理ユーザー用
-
フォーム認証を使用する一般ユーザー向け
-
認証を必要としないゲストユーザー用
管理ユーザー用に定義されたエントリポイントは、/admin/**形式のURLを保護して、ADMINの役割を持つユーザーのみを許可し、authenticationEntryPoint()を使用して設定されたタイプBasicAuthenticationEntryPointのエントリポイントでのHTTP基本認証を必要とします。 )sメソッド:
@Configuration
@Order(1)
public static class App1ConfigurationAdapter extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/admin/**")
.authorizeRequests().anyRequest().hasRole("ADMIN")
.and().httpBasic().authenticationEntryPoint(authenticationEntryPoint());
}
@Bean
public AuthenticationEntryPoint authenticationEntryPoint(){
BasicAuthenticationEntryPoint entryPoint =
new BasicAuthenticationEntryPoint();
entryPoint.setRealmName("admin realm");
return entryPoint;
}
}
各静的クラスの@Orderアノテーションは、要求されたURLに一致するものを見つけるために構成が考慮される順序を示します。 The order value for each class must be unique.
タイプBasicAuthenticationEntryPointのBeanでは、プロパティrealNameを設定する必要があります。
3.2. 複数のエントリポイント、同じHTTP要素
次に、フォーム認証を使用してUSERロールを持つ通常のユーザーがアクセスできるフォーム/user/**のURLの構成を定義しましょう。
@Configuration
@Order(2)
public static class App2ConfigurationAdapter extends WebSecurityConfigurerAdapter {
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/user/**")
.authorizeRequests().anyRequest().hasRole("USER")
.and()
// formLogin configuration
.and()
.exceptionHandling()
.defaultAuthenticationEntryPointFor(
loginUrlauthenticationEntryPointWithWarning(),
new AntPathRequestMatcher("/user/private/**"))
.defaultAuthenticationEntryPointFor(
loginUrlauthenticationEntryPoint(),
new AntPathRequestMatcher("/user/general/**"));
}
}
ご覧のとおり、authenticationEntryPoint()メソッド以外に、エントリポイントを定義する別の方法は、defaultAuthenticationEntryPointFor()メソッドを使用することです。 これにより、RequestMatcherオブジェクトに基づいてさまざまな条件に一致する複数のエントリポイントを定義できます。
RequestMatcherインターフェイスには、一致するパス、メディアタイプ、正規表現など、さまざまなタイプの条件に基づいた実装があります。 この例では、AntPathRequestMatchを使用して、/user/private/と/user/general/の形式のURLに2つの異なるエントリポイントを設定しました。
次に、同じ静的構成クラスでエントリポイントBeanを定義する必要があります。
@Bean
public AuthenticationEntryPoint loginUrlauthenticationEntryPoint(){
return new LoginUrlAuthenticationEntryPoint("/userLogin");
}
@Bean
public AuthenticationEntryPoint loginUrlauthenticationEntryPointWithWarning(){
return new LoginUrlAuthenticationEntryPoint("/userLoginWithWarning");
}
ここでの主なポイントは、これらの複数のエントリポイントを設定する方法です。必ずしも各エントリポイントの実装の詳細ではありません。
この場合、エントリポイントは両方ともタイプLoginUrlAuthenticationEntryPointであり、異なるログインページURLを使用します。単純なログインページの場合は/userLogin、ログインページの場合は/userLoginWithWarningで、次の場合にも警告が表示されます。 /user/のプライベートURLにアクセスしようとしています。
この構成では、/userLoginおよび/userLoginWithWarningMVCマッピングと2つのページを標準のログインフォームで定義する必要もあります。
フォーム認証の場合、ログイン処理URLなどの構成に必要なURLも、/user/**形式に従うか、アクセスできるように構成する必要があることを覚えておくことが非常に重要です。
適切なロールを持たないユーザーが保護されたURLにアクセスしようとすると、上記の両方の構成で/403URLにリダイレクトされます。
Be careful to use unique names for the beans even if they are in different static classes、それ以外の場合、一方が他方をオーバーライドします。
3.3. 新しいHTTP要素、エントリポイントなし
最後に、認証されていないユーザーを含むすべてのタイプのユーザーを許可する、/guest/**形式のURLの3番目の構成を定義しましょう。
@Configuration
@Order(3)
public static class App3ConfigurationAdapter extends WebSecurityConfigurerAdapter {
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/guest/**").authorizeRequests().anyRequest().permitAll();
}
}
3.4. XML構成
前のセクションの3つのHttpSecurityインスタンスの同等のXML構成を見てみましょう。
予想どおり、これには3つの個別のXML<http>ブロックが含まれます。
/admin/** URLの場合、XML構成はhttp-basic要素のentry-point-ref属性を使用します。
ここで注意すべきことは、XML構成を使用する場合、ロールはROLE
defaultAuthenticationEntryPointFor()メソッドに直接相当するものがないため、/user/** URLの構成をxmlで2つのhttpブロックに分割する必要があります。
URL / user / general / **の構成は次のとおりです。
//form-login configuration
/user/private/** URLについては、同様の構成を定義できます。
//form-login configuration
/guest/** URLの場合、http要素があります。
ここで重要なのは、少なくとも1つのXML<http>ブロックが/ **パターンと一致する必要があることです。
4. 保護されたURLへのアクセス
4.1. MVC構成
保護したURLパターンに一致するリクエストマッピングを作成しましょう。
@Controller
public class PagesController {
@GetMapping("/admin/myAdminPage")
public String getAdminPage() {
return "multipleHttpElems/myAdminPage";
}
@GetMapping("/user/general/myUserPage")
public String getUserPage() {
return "multipleHttpElems/myUserPage";
}
@GetMapping("/user/private/myPrivateUserPage")
public String getPrivateUserPage() {
return "multipleHttpElems/myPrivateUserPage";
}
@GetMapping("/guest/myGuestPage")
public String getGuestPage() {
return "multipleHttpElems/myGuestPage";
}
@GetMapping("/multipleHttpLinks")
public String getMultipleHttpLinksPage() {
return "multipleHttpElems/multipleHttpLinks";
}
}
/multipleHttpLinksマッピングは、保護されたURLへのリンクを含む単純なHTMLページを返します。
保護されたURLに対応する各HTMLページには、単純なテキストとバックリンクがあります。
Welcome admin!
Back to links
4.2. アプリケーションの初期化
この例をSpringBootアプリケーションとして実行するので、mainメソッドを使用してクラスを定義しましょう。
@SpringBootApplication
public class MultipleEntryPointsApplication {
public static void main(String[] args) {
SpringApplication.run(MultipleEntryPointsApplication.class, args);
}
}
XML構成を使用する場合は、メインクラスに@ImportResource(\{“classpath*:spring-security-multiple-entry.xml”})アノテーションを追加する必要もあります。
4.3. セキュリティ構成のテスト
保護されたURLをテストするために使用できるJUnitテストクラスを設定しましょう。
@RunWith(SpringRunner.class)
@WebAppConfiguration
@SpringBootTest(classes = MultipleEntryPointsApplication.class)
public class MultipleEntryPointsTest {
@Autowired
private WebApplicationContext wac;
@Autowired
private FilterChainProxy springSecurityFilterChain;
private MockMvc mockMvc;
@Before
public void setup() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac)
.addFilter(springSecurityFilterChain).build();
}
}
次に、adminユーザーを使用してURLをテストしましょう。
HTTP基本認証なしで/admin/adminPage URLを要求する場合、未承認のステータスコードを受け取ることを期待する必要があり、認証を追加した後、ステータスコードは200OKになるはずです。
adminユーザーで/user/userPage URLにアクセスしようとすると、ステータス302Forbiddenを受け取るはずです。
@Test
public void whenTestAdminCredentials_thenOk() throws Exception {
mockMvc.perform(get("/admin/myAdminPage")).andExpect(status().isUnauthorized());
mockMvc.perform(get("/admin/myAdminPage")
.with(httpBasic("admin", "adminPass"))).andExpect(status().isOk());
mockMvc.perform(get("/user/myUserPage")
.with(user("admin").password("adminPass").roles("ADMIN")))
.andExpect(status().isForbidden());
}
通常のユーザー資格情報を使用してURLにアクセスする同様のテストを作成しましょう。
@Test
public void whenTestUserCredentials_thenOk() throws Exception {
mockMvc.perform(get("/user/general/myUserPage")).andExpect(status().isFound());
mockMvc.perform(get("/user/general/myUserPage")
.with(user("user").password("userPass").roles("USER")))
.andExpect(status().isOk());
mockMvc.perform(get("/admin/myAdminPage")
.with(user("user").password("userPass").roles("USER")))
.andExpect(status().isForbidden());
}
2番目のテストでは、フォーム認証がないと、Spring Securityがログインフォームにリダイレクトするため、ステータスがUnauthorizedではなく302 Foundになることがわかります。
最後に、/guest/guestPage URLにアクセスして、3種類すべての認証を行い、ステータスが200OKであることを確認するテストを作成しましょう。
@Test
public void givenAnyUser_whenGetGuestPage_thenOk() throws Exception {
mockMvc.perform(get("/guest/myGuestPage")).andExpect(status().isOk());
mockMvc.perform(get("/guest/myGuestPage")
.with(user("user").password("userPass").roles("USER")))
.andExpect(status().isOk());
mockMvc.perform(get("/guest/myGuestPage")
.with(httpBasic("admin", "adminPass")))
.andExpect(status().isOk());
}
5. 結論
このチュートリアルでは、Spring Securityを使用するときに複数のエントリポイントを構成する方法を示しました。
例の完全なソースコードはover on GitHubにあります。 アプリケーションを実行するには、pom.xmlのMultipleEntryPointsApplicationstart-classタグのコメントを解除し、コマンドmvn spring-boot:runを実行してから、/multipleHttpLinks URL.にアクセスします。
HTTP基本認証を使用している場合はログアウトできないため、ブラウザを閉じて再度開いてこの認証を削除する必要があります。
JUnitテストを実行するには、次のコマンドで定義済みのMavenプロファイルentryPointsを使用します。
mvn clean install -PentryPoints