リアクティブアプリケーション用のSpringSecurity5
1. 序章
この記事では、リアクティブアプリケーションを保護するための Spring Security5フレームワークの新機能について説明します。 このリリースは、Spring5およびSpringBoot2に対応しています。
この記事では、Spring5フレームワークの新機能であるリアクティブアプリケーション自体については詳しく説明しません。 詳細については、記事 Intro to ReactorCoreを確認してください。
2. Mavenのセットアップ
Spring Bootスターターを使用して、必要なすべての依存関係とともにプロジェクトをブートストラップします。
基本的なセットアップには、親宣言、Webスターター、およびセキュリティスターターの依存関係が必要です。 SpringSecurityテストフレームワークも必要です。
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.1</version>
<relativePath/>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</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セキュリティスターターの現在のバージョンは、MavenCentralで確認できます。
3. プロジェクトの設定
3.1. リアクティブアプリケーションのブートストラップ
標準の@SpringBootApplication構成は使用しませんが、代わりにNettyベースのWebサーバーを構成します。 Nettyは、非同期NIOベースのフレームワークであり、リアクティブアプリケーションの優れた基盤です。
@EnableWebFlux アノテーションは、アプリケーションの標準のSpringWebReactive構成を有効にします。
@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()チェーンを呼び出して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セキュリティ構成クラス
基本的なSpringSecurity構成では、構成クラスSecurityConfigを作成します。
Spring Security 5でWebFluxサポートを有効にするには、@EnableWebFluxSecurityアノテーションを指定するだけです。
@EnableWebFluxSecurity
public class SecurityConfig {
// ...
}
これで、クラスServerHttpSecurityを利用してセキュリティ構成を構築できます。
ServerHttpSecurity は、いくつかの適切なデフォルトですでに事前構成されているため、この構成を完全にスキップできます。 ただし、初心者向けに、次の最小限の構成を提供します。
@Bean
public SecurityWebFilterChain securityWebFilterChain(
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);
}
私たちはリアクティブな土地にいるので、ユーザー詳細サービスもリアクティブである必要があります。 ReactiveUserDetailsService インターフェースをチェックアウトすると、そのfindByUsernameメソッドが実際にMonoパブリッシャーを返すことがわかります:
public interface ReactiveUserDetailsService {
Mono<UserDetails> findByUsername(String username);
}
これで、アプリケーションを実行して、通常のHTTP基本認証フォームを確認できます。
4. スタイル付きログインフォーム
Spring Security 5の小さいながらも目覚ましい改善は、Bootstrap4CSSフレームワークを使用する新しいスタイルのログインフォームです。 ログインフォームのスタイルシートはCDNにリンクしているため、インターネットに接続した場合にのみ改善が見られます。
新しいログインフォームを使用するには、対応する formLogin()ビルダーメソッドをServerHttpSecurityビルダーに追加しましょう。
public SecurityWebFilterChain securityWebFilterChain(
ServerHttpSecurity http) {
return http.authorizeExchange()
.anyExchange().authenticated()
.and().formLogin()
.and().build();
}
アプリケーションのメインページを開くと、以前のバージョンのSpringSecurity以降に使用されていたデフォルトのフォームよりもはるかに見栄えがよいことがわかります。
これは本番環境に対応したフォームではありませんが、アプリケーションの優れたブートストラップであることに注意してください。
ここでログインしてからhttp:// localhost:8080 / logout URLにアクセスすると、ログアウト確認フォームが表示されます。これもスタイルが設定されています。
5. リアクティブコントローラーのセキュリティ
認証フォームの背後にあるものを確認するために、ユーザーに挨拶する単純なリアクティブコントローラーを実装しましょう。
@RestController
public class GreetingController {
@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でログインすると、認証されたすべてのユーザーがアクセスできるようになっているため、両方が最初の挨拶を監視していることがわかります。
ただし、管理者ユーザーのみがhttp:// localhost:8080 / admin URLにアクセスして、挨拶を見ることができます。
6. リアクティブメソッドのセキュリティ
URLを保護する方法を見てきましたが、メソッドについてはどうでしょうか。
リアクティブメソッドのメソッドベースのセキュリティを有効にするには、@EnableReactiveMethodSecurityアノテーションをSecurityConfigクラスに追加するだけです。
@EnableWebFluxSecurity
@EnableReactiveMethodSecurity
public class SecurityConfig {
// ...
}
次に、次のコンテンツを使用してリアクティブグリーティングサービスを作成しましょう。
@Service
public class GreetingService {
public Mono<String> greet() {
return Mono.just("Hello from service!");
}
}
これをコントローラーに挿入し、 http:// localhost:8080 / greetingService にアクセスして、実際に機能することを確認できます。
@RestController
public class GreetingController {
private GreetingService greetingService
// constructor...
@GetMapping("/greetingService")
public Mono<String> greetingService() {
return greetingService.greet();
}
}
ただし、ADMINロールを使用してサービスメソッドに@PreAuthorizeアノテーションを追加すると、通常のユーザーはgreetサービスのURLにアクセスできなくなります。
@Service
public class GreetingService {
@PreAuthorize("hasRole('ADMIN')")
public Mono<String> greet() {
// ...
}
}
7. テストでユーザーをあざける
リアクティブSpringアプリケーションのテストがいかに簡単かを確認しましょう。
まず、注入されたアプリケーションコンテキストを使用してテストを作成します。
@ContextConfiguration(classes = SpringSecurity5Application.class)
public class SecurityTest {
@Autowired
ApplicationContext context;
// ...
}
次に、Spring5テストフレームワークの機能である単純なリアクティブWebテストクライアントをセットアップします。
@Before
public void setup() {
this.webTestClient = WebTestClient
.bindToApplicationContext(this.context)
.configureClient()
.build();
}
これにより、許可されていないユーザーがアプリケーションのメインページからログインページにリダイレクトされていることをすばやく確認できます。
@Test
public void whenNoCredentials_thenRedirectToLogin() {
webTestClient.get()
.uri("/")
.exchange()
.expectStatus().is3xxRedirection();
}
ここで、 @WithMockUser アノテーションをテストメソッドに追加すると、このメソッドの認証済みユーザーを提供できます。
このユーザーのログインとパスワードはそれぞれuserとpasswordであり、役割はUSERです。 もちろん、これはすべて@WithMockUserアノテーションパラメーターを使用して構成できます。
これで、許可されたユーザーに挨拶が表示されることを確認できます。
@Test
@WithMockUser
public void whenHasCredentials_thenSeesGreeting() {
webTestClient.get()
.uri("/")
.exchange()
.expectStatus().isOk()
.expectBody(String.class).isEqualTo("Hello, user");
}
@WithMockUser アノテーションは、SpringSecurity4以降で使用できます。 ただし、これはSpring Security 5でも更新され、リアクティブエンドポイントとメソッドをカバーしています。
8. 結論
このチュートリアルでは、特にリアクティブプログラミングの分野で、次のSpringSecurity5リリースの新機能を発見しました。
いつものように、記事のソースコードはGitHubでから入手できます。