リアクティブアプリケーション用のSpring Security 5

1前書き

この記事では、リアクティブアプリケーションを保護するためのhttps://projects.spring.io/spring-security/[Spring Security 5]フレームワークの新機能について説明します。このリリースは、Spring 5とSpring Boot 2に対応しています。

この記事では、Spring 5フレームワークの新機能であるリアクティブアプリケーション自体については詳しく説明しません。詳しくは記事リンク:/reactor-core[炉心の紹介]をチェックしてください。

2 Mavenのセットアップ

Spring Bootスターターを使用して、必要なすべての依存関係と共にプロジェクトをブートストラップします。

基本設定には、親宣言、Webスターター、およびセキュリティースターターの依存関係が必要です。 Spring Securityのテストフレームワークも必要です。

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.0.0.RELEASE</version>
    <relativePath/>
</parent>

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

現在のバージョンのSpring Bootセキュリティスターターをチェックアウトすることができます。 -boot-starter-security%22[Maven Centralで終わりました]。

3プロジェクト設定

3.1. リアクティブアプリケーションのブートストラップ

標準の@ @ SpringBootApplication__設定を使用するのではなく、代わりにNettyベースのWebサーバーを設定します。 Nettyは、リアクティブアプリケーションの基礎となる非同期NIOベースのフレームワークです。

@ EnableWebFlux アノテーションは、アプリケーションの標準のSpring Web Reactive設定を有効にします。

@ComponentScan(basePackages = {"com.baeldung.security"})
@EnableWebFlux
public class SpringSecurity5Application {

    public static void main(String[]args) {
        try (AnnotationConfigApplicationContext context
         = new AnnotationConfigApplicationContext(
            SpringSecurity5Application.class)) {

            context.getBean(NettyContext.class).onClose().block();
        }
    }

ここでは、新しいアプリケーションコンテキストを作成し、Nettyコンテキストで .onClose()。block() chainを呼び出してNettyがシャットダウンするのを待ちます。

Nettyがシャットダウンされた後、コンテキストは try-with-resources ブロックを使用して自動的に閉じられます。

また、NettyベースのHTTPサーバー、HTTPリクエスト用のハンドラー、およびサーバーとハンドラー間のアダプターを作成する必要があります。

@Bean
public NettyContext nettyContext(ApplicationContext context) {
    HttpHandler handler = WebHttpHandlerBuilder
      .applicationContext(context).build();
    ReactorHttpHandlerAdapter adapter
      = new ReactorHttpHandlerAdapter(handler);
    HttpServer httpServer = HttpServer.create("localhost", 8080);
    return httpServer.newHandler(adapter).block();
}

3.2. Springセキュリティ設定クラス

Spring Securityの基本的な設定のために、設定クラス SecurityConfig を作成します。

Spring Security 5でWebFluxサポートを有効にするには、 @ EnableWebFluxSecurity アノテーションを指定するだけです

@EnableWebFluxSecurity
public class SecurityConfig {
   //...
}
  • これでセキュリティ設定を構築するためにクラス ServerHttpSecurity を利用することができます。**

  • このクラスはSpring 5の新機能です。** これは HttpSecurity ビルダーに似ていますが、WebFluxアプリケーションに対してのみ有効です。

ServerHttpSecurity はすでにいくつかの正しいデフォルトで事前設定されているので、この設定を完全にスキップすることができます。しかし、初心者のために、以下の最小限の設定を提供します。

@Bean
public SecurityWebFilterChain securitygWebFilterChain(
  ServerHttpSecurity http) {
    return http.authorizeExchange()
      .anyExchange().authenticated()
      .and().build();
}

また、ユーザー詳細サービスも必要です。 Spring Securityは便利なモックユーザービルダーとユーザー詳細サービスのインメモリ実装を提供します。

@Bean
public MapReactiveUserDetailsService userDetailsService() {
    UserDetails user = User
      .withUsername("user")
      .password(passwordEncoder().encode("password"))
      .roles("USER")
      .build();
    return new MapReactiveUserDetailsService(user);
}

私たちは反応的な土地にいるので、ユーザー詳細サービスも反応的であるべきです。 ReactiveUserDetailsS​​ervice インターフェースをチェックアウトすると、その findByUsername メソッドが実際に Mono パブリッシャーを返すことがわかります。

public interface ReactiveUserDetailsService {

    Mono<UserDetails> findByUsername(String username);
}

これでアプリケーションを実行して、通常のHTTP基本認証フォームを観察することができます。

4スタイル付きログインフォーム

Spring Security 5の小さいながらも目立った改善は、Bootstrap 4 CSSフレームワークを使用する新しいスタイルのログインフォームです。ログインフォームのスタイルシートはCDNにリンクしているので、インターネットに接続した場合にのみ改善が見られます。

新しいログインフォームを使用するには、対応する formLogin() ビルダーメソッドを ServerHttpSecurity ビルダーに追加します。

public SecurityWebFilterChain securitygWebFilterChain(
  ServerHttpSecurity http) {
    return http.authorizeExchange()
      .anyExchange().authenticated()
      .and().formLogin()
      .and().build();
}

ここでアプリケーションのメインページを開くと、以前のバージョンのSpring Security以降に使用されてきたデフォルトのフォームよりもはるかに見栄えがよくなります。

リンク:/uploads/2017-11-16__00-10-07.png%20776w[]

これは本番用のフォームではありませんが、アプリケーションの優れたブートストラップです。

ログインしてからhttp://localhost:8080/logoutのURLにアクセスすると、ログアウト確認フォームが表示されます。

5リアクティブコントローラセキュリティ

認証フォームの背後にあるものを見るために、ユーザーに挨拶する簡単なリアクティブコントローラを実装しましょう。

@RestController
public class GreetController {

    @GetMapping("/")
    public Mono<String> greet(Mono<Principal> principal) {
        return principal
          .map(Principal::getName)
          .map(name -> String.format("Hello, %s", name));
    }

}

ログインすると、グリーティングが表示されます。管理者だけがアクセスできる別のリアクティブハンドラを追加しましょう。

@GetMapping("/admin")
public Mono<String> greetAdmin(Mono<Principal> principal) {
    return principal
      .map(Principal::getName)
      .map(name -> String.format("Admin access: %s", name));
}

それでは、ユーザー詳細サービスで、ロール ADMIN :を持つ2番目のユーザーを作成しましょう。

UserDetails admin = User.withDefaultPasswordEncoder()
  .username("admin")
  .password("password")
  .roles("ADMIN")
  .build();

これで、ユーザーに ROLE ADMIN__権限​​を付与することを要求する管理者URLの照合規則を追加できます。

  • .anyExchange() ** チェーンコールの前にマッチャーを配置する必要があることに注意してください。この呼び出しは、他のマッチャーによってまだカバーされていない他のすべてのURLに適用されます。

return http.authorizeExchange()
  .pathMatchers("/admin").hasAuthority("ROLE__ADMIN")
  .anyExchange().authenticated()
  .and().formLogin()
  .and().build();

ここで user または admin を使用してログインした場合、認証されたすべてのユーザーがアクセスできるようになったため、両方とも初期グリーティングを確認していることがわかります。

  • しかし、 admin ユーザーだけがhttp://localhost:8080/adminのURLにアクセスして彼女のグリーティングを見ることができます**

6. 反応的メソッドセキュリティ

URLをどのように保護できるかを見てきましたが、メソッドはどうでしょうか。

リアクティブメソッドに対してメソッドベースのセキュリティを有効にするには、 SecurityConfigクラスに @ EnableReactiveMethodSecurity__アノテーションを追加するだけです。

@EnableWebFluxSecurity
@EnableReactiveMethodSecurity
public class SecurityConfig {
   //...
}

それでは、次の内容の反応的グリーティングサービスを作成しましょう。

@Service
public class GreetService {

    public Mono<String> greet() {
        return Mono.just("Hello from service!");
    }
}

それをコントローラに注入し、http://localhost:8080/greetServiceにアクセスして、実際に動作することを確認します。

@RestController
public class GreetController {

    private GreetService greetService

    @GetMapping("/greetService")
    public Mono<String> greetService() {
        return greetService.greet();
    }

   //standard constructors...
}

ただし、 ADMIN ロールを使用してサービスメソッドに @ PreAuthorize アノテーションを追加すると、グリーティングサービスのURLに通常のユーザーがアクセスできなくなります。

@Service
public class GreetService {

@PreAuthorize("hasRole('ADMIN')")
public Mono<String> greet() {
   //...
}

7. テストでユーザーをモックする

リアクティブSpringアプリケーションをテストするのがどれほど簡単かを調べてみましょう。

まず、注入されたアプリケーションコンテキストを使ってテストを作成します。

@ContextConfiguration(classes = SpringSecurity5Application.class)
public class SecurityTest {

    @Autowired
    ApplicationContext context;

   //...
}

これで、Spring 5のテストフレームワークの機能である単純なリアクティブWebテストクライアントをセットアップしましょう。

@Before
public void setup() {
    this.rest = WebTestClient
      .bindToApplicationContext(this.context)
      .configureClient()
      .build();
}

これにより、権限のないユーザーがアプリケーションのメインページからログインページにリダイレクトされていることをすばやく確認できます。

@Test
public void whenNoCredentials__thenRedirectToLogin() {
    this.rest.get()
      .uri("/")
      .exchange()
      .expectStatus().is3xxRedirection();
}
  • テストメソッドに @ MockWithUser アノテーションを追加した場合、このメソッドに認証済みユーザーを提供できます。

このユーザーのログイン名とパスワードはそれぞれ user password で、役割は USER です。もちろん、これはすべて @ MockWithUser アノテーションパラメータで設定できます。

これで、許可されたユーザーにグリーティングが表示されていることを確認できます。

@Test
@WithMockUser
public void whenHasCredentials__thenSeesGreeting() {
    this.rest.get()
      .uri("/")
      .exchange()
      .expectStatus().isOk()
      .expectBody(String.class).isEqualTo("Hello, user");
}

@ WithMockUser アノテーションは、Spring Security 4以降で利用可能です。

ただし、Spring Security 5ではリアクティブエンドポイントとメソッドをカバーするように更新されました。

8結論

このチュートリアルでは、次のSpring Security 5リリースの新機能を発見しました。特にリアクティブプログラミングの分野では。

いつものように、この記事のソースコードはhttp://github.com/eugenp/tutorials/tree/master/spring-5-reactive-security[GitHubで利用可能]です。

前の投稿:JUnitParamsの紹介
次の投稿:コレクションからのJava Null-Safeストリーム