1概要

このクイックチュートリアルでは、Spring Securityアプリケーションで複数のエントリポイントを定義する方法を見ていきます。

これは主に、

WebSecurityConfigurerAdapter

クラスを複数回拡張することによって、XML構成ファイルまたは複数の

HttpSecurity

インスタンスに複数の

http

ブロックを定義することを伴います。


2 Mavenの依存関係

開発には、以下の依存関係が必要になります。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
    <version>2.0.0.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <version>2.0.0.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
    <version>2.0.0.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <version>2.0.0.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-test</artifactId>
    <version>5.0.4.RELEASE</version>
</dependency>


spring-boot-starter-security

、https://の最新バージョンsearch.maven.org/classic/#search%7Cga%7C1%7Ca%3A%22spring-boot-starter-web%22%20AND%20g%3A%22org.springframework.boot%22[spring-boot-starter-web]、https://search.maven.org/classic/#search%7Cga%7C1%7Ca%3A%22spring-boot-starter-thymeleaf%22[spring-boot-starter-thymeleaf]、https://検索。 maven.org/classic/#search%7Cga%7C1%7Ca%3A%22spring-boot-starter-test%22[spring-boot-starter-test]、https://search.maven.org/classic/#search %7Cga%7C1%7Ca%3A%22スプリングセキュリティテスト%22[スプリングセキュリティテスト]はMaven Centralからダウンロードできます。


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();
    }
}

それでは、セキュリティ設定で** 複数のエントリポイントを定義する方法を見てみましょう。

ここでは基本認証を使用した例を使用します。また、設定では

Spring Securityが複数のHTTP要素の定義

をサポートしていることを活用します。

Java設定を使用する場合、複数のセキュリティレルムを定義する方法は、

WebSecurityConfigurerAdapter

基本クラスを拡張する複数の

@ Configuration

クラスを作成することです。これらのクラスは静的にすることができ、メイン設定の中に置くことができます。

1つのアプリケーションに複数のエントリポイントがある主な動機は、アプリケーションのさまざまな部分にアクセスできるさまざまな種類のユーザーがいるかどうかです。

それぞれ異なる権限と認証モードを持つ3つのエントリポイントを持つ構成を定義しましょう。

  • HTTP基本認証を使用している管理ユーザー用

  • フォーム認証を使用する一般ユーザー向け

  • 認証を必要としないゲストユーザ用

管理ユーザー用に定義されたエントリポイントは、ADMINのロールを持つユーザーのみを許可するように

/admin/



の形式のURLを保護し、

authenticationEntryPoint()

メソッドを使用して設定される:

@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に一致するものを見つけるために構成が考慮される順序を示します。 ** 各クラスの

order

値は一意である必要があります。

タイプ

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()メソッド以外のエントリポイントを定義するもう1つの方法は、

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

および

/userLoginWithWarning

MVCマッピングと、標準ログインフォームを使用した2ページの定義も必要になります。

フォーム認証では、設定に必要なURL(ログイン処理URLなど)も

/user/



の形式に従う必要があるか、またはその他の方法でアクセスできるように設定する必要があることを忘れないでください。

適切な役割を持たないユーザーが保護されたURLにアクセスしようとすると、上記の両方の設定が

/403

URLにリダイレクトされます。

  • それらが異なる静的クラスに属している場合であっても、Beanに固有の名前を使用するように注意してください。** そうでない場合、一方が他方をオーバーライドします。

** 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

属性を使用します。

<security:http pattern="/admin/** ** " use-expressions="true" auto-config="true">
    <security:intercept-url pattern="/** ** " access="hasRole('ROLE__ADMIN')"/>
    <security:http-basic entry-point-ref="authenticationEntryPoint"/>
</security:http>

<bean id="authenticationEntryPoint"
  class="org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint">
     <property name="realmName" value="admin realm"/>
</bean>

ここで注目に値するのは、XML設定を使用する場合、ロールは

ROLE

<ROLE

NAME>

の形式でなければならないということです。


defaultAuthenticationEntryPointFor()

メソッドに直接相当するものがないため、

/user/



URLの設定はxmlで2つの

http

ブロックに分割する必要があります。

URL/user/general/


の設定は次のとおりです。

<security:http pattern="/user/general/** ** " use-expressions="true" auto-config="true"
  entry-point-ref="loginUrlAuthenticationEntryPoint">
    <security:intercept-url pattern="/** ** " access="hasRole('ROLE__USER')"/>
   //form-login configuration
</security:http>

<bean id="loginUrlAuthenticationEntryPoint"
  class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
  <constructor-arg name="loginFormUrl" value="/userLogin"/>
</bean>


/user/private/



URLについても、同様の設定を定義できます。

<security:http pattern="/user/private/** ** " use-expressions="true" auto-config="true"
  entry-point-ref="loginUrlAuthenticationEntryPointWithWarning">
    <security:intercept-url pattern="/** ** " access="hasRole('ROLE__USER')"/>
   //form-login configuration
</security:http>

<bean id="loginUrlAuthenticationEntryPointWithWarning"
  class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
    <constructor-arg name="loginFormUrl" value="/userLoginWithWarning"/>
</bean>


/guest/



URLの場合、

http

要素があります。

<security:http pattern="/** ** " use-expressions="true" auto-config="true">
    <security:intercept-url pattern="/guest/** ** " access="permitAll()"/>
</security: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ページを返します。

<a th:href="@{/admin/myAdminPage}">Admin page</a>
<a th:href="@{/user/general/myUserPage}">User page</a>
<a th:href="@{/user/private/myPrivateUserPage}">Private user page</a>
<a th:href="@{/guest/myGuestPage}">Guest page</a>

保護されたURLに対応する各HTMLページには、単純なテキストとバックリンクがあります。

Welcome admin!

<a th:href="@{/multipleHttpLinks}" >Back to links</a>

** 4.2. アプリケーションの初期化

この例はSpring Bootアプリケーションとして実行しますので、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を要求すると、Unauthorizedステータスコードを受け取ることになります。認証を追加した後、ステータスコードは200 OKになります。

adminユーザーで

/user/userPage

URLにアクセスしようとすると、ステータス302 Forbiddenが表示されます。

@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種類の認証すべてにアクセスし、ステータスが200 OKになることを確認するテストを作成しましょう。

@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を使用するときに複数のエントリポイントを設定する方法を説明しました。

例の完全なソースコードはhttps://github.com/eugenp/tutorials/tree/master/spring-security-mvc-boot[GitHubに載っています]。アプリケーションを実行するには、

pom.xml内の

MultipleEntryPointsApplication


start-class

タグのコメントを外して

mvn spring-boot:run

コマンドを実行し、

/multipleHttpLinks

URL


.

__にアクセスします。

HTTP基本認証を使用しているときはログアウトできないため、この認証を削除するにはブラウザを閉じて再度開く必要があります。

JUnitテストを実行するには、次のコマンドで定義済みのMavenプロファイル

entryPoints

を使用します。

mvn clean install -PentryPoints__