Spring Boot は、Web アプリケーションの開発に最適です。組み込み Tomcat、Jetty、Undertow、Netty を使用して、自己完結型の HTTP サーバーを作成できます。ほとんどの Web アプリケーションは、spring-boot-starter-web モジュールを使用して、すばやく起動して実行します。spring-boot-starter-webflux モジュールを使用して、リアクティブ Web アプリケーションを構築することもできます。

Spring Boot Web アプリケーションをまだ開発していない場合は、入門セクションの "Hello World!" の例に従うことができます。

1. サーブレット Web アプリケーション

サーブレットベースの Web アプリケーションを構築する場合は、Spring MVC または Jersey 用の Spring Boot の自動構成を利用できます。

1.1. 「Spring Web MVC フレームワーク」

Spring Web MVC フレームワーク ( "Spring MVC" と呼ばれることが多い) は、リッチな「モデル、ビュー、コントローラー」Web フレームワークです。Spring MVC を使用すると、受信 HTTP リクエストを処理するための特別な @Controller または @RestController Bean を作成できます。コントローラーのメソッドは、@RequestMapping アノテーションを使用して HTTP にマップされます。

次のコードは、JSON データを提供する典型的な @RestController を示しています。

import java.util.List;

import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/users")
public class MyRestController {

    private final UserRepository userRepository;

    private final CustomerRepository customerRepository;

    public MyRestController(UserRepository userRepository, CustomerRepository customerRepository) {
        this.userRepository = userRepository;
        this.customerRepository = customerRepository;
    }

    @GetMapping("/{user}")
    public User getUser(@PathVariable Long userId) {
        return this.userRepository.findById(userId).get();
    }

    @GetMapping("/{user}/customers")
    public List<Customer> getUserCustomers(@PathVariable Long userId) {
        return this.userRepository.findById(userId).map(this.customerRepository::findByUser).get();
    }

    @DeleteMapping("/{user}")
    public void deleteUser(@PathVariable Long userId) {
        this.userRepository.deleteById(userId);
    }

}

Spring MVC はコア Spring Framework の一部であり、詳細情報はリファレンスドキュメントで入手できます。spring.io/guides で入手可能な Spring MVC をカバーするガイドもいくつかあります。

1.1.1. Spring MVC 自動構成

Spring Boot は、ほとんどのアプリケーションでうまく機能する Spring MVC の自動構成を提供します。

自動構成により、Spring のデフォルトに加えて次の機能が追加されます。

これらの Spring Boot MVC のカスタマイズを保持し、さらに MVC のカスタマイズ(インターセプター、フォーマッター、View Controller、およびその他の機能)を作成する場合は、@EnableWebMvc なしで型 WebMvcConfigurer の独自の @Configuration クラスを追加できます。

RequestMappingHandlerMappingRequestMappingHandlerAdapterExceptionHandlerExceptionResolver のカスタムインスタンスを提供し、それでも Spring Boot MVC のカスタマイズを維持したい場合は、型 WebMvcRegistrations の Bean を宣言し、それを使用してこれらのコンポーネントのカスタムインスタンスを提供できます。

Spring MVC を完全に制御したい場合は、@EnableWebMvc でアノテーションを付けた独自の @Configuration を追加するか、@EnableWebMvc の Javadoc に従って、独自の @Configuration アノテーション付き DelegatingWebMvcConfiguration を追加できます。

Spring MVC は、application.properties または application.yaml ファイルから値を変換するために使用されるものとは異なる ConversionService を使用します。これは、PeriodDurationDataSize コンバーターが使用できず、@DurationUnit および @DataSizeUnit アノテーションが無視されることを意味します。

Spring MVC が使用する ConversionService をカスタマイズする場合は、WebMvcConfigurer Bean に addFormatters メソッドを提供できます。このメソッドから、好きなコンバーターを登録したり、ApplicationConversionService で利用可能な静的メソッドに委譲したりできます。

1.1.2. HttpMessageConverters

Spring MVC は、HttpMessageConverter インターフェースを使用して HTTP リクエストとレスポンスを変換します。適切なデフォルトは、すぐに含まれています。例: オブジェクトは、JSON (Jackson ライブラリを使用) または XML (使用可能な場合は Jackson XML 拡張を使用するか、Jackson XML 拡張が使用できない場合は JAXB を使用) に自動的に変換できます。デフォルトでは、文字列は UTF-8 でエンコードされます。

コンバーターを追加またはカスタマイズする必要がある場合は、次のように、Spring Boot の HttpMessageConverters クラスを使用できます。

import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;

@Configuration(proxyBeanMethods = false)
public class MyHttpMessageConvertersConfiguration {

    @Bean
    public HttpMessageConverters customConverters() {
        HttpMessageConverter<?> additional = new AdditionalHttpMessageConverter();
        HttpMessageConverter<?> another = new AnotherHttpMessageConverter();
        return new HttpMessageConverters(additional, another);
    }

}

コンテキストに存在する HttpMessageConverter Bean は、コンバーターのリストに追加されます。同じ方法でデフォルトのコンバーターをオーバーライドすることもできます。

1.1.3. カスタム JSON シリアライザーとデシリアライザー

Jackson を使用して JSON データをシリアライズおよびデシリアライズする場合、独自の JsonSerializer および JsonDeserializer クラスを作成することができます。カスタムシリアライザーは通常、モジュールを介して Jackson に登録されます [GitHub] (英語) が、Spring Boot は Spring Bean を直接登録しやすくする代替 @JsonComponent アノテーションを提供します。

JsonSerializerJsonDeserializer または KeyDeserializer 実装で @JsonComponent アノテーションを直接使用できます。次の例に示すように、内部クラスとしてシリアライザー / デシリアライザーを含むクラスでも使用できます。

import java.io.IOException;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.ObjectCodec;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;

import org.springframework.boot.jackson.JsonComponent;

@JsonComponent
public class MyJsonComponent {

    public static class Serializer extends JsonSerializer<MyObject> {

        @Override
        public void serialize(MyObject value, JsonGenerator jgen, SerializerProvider serializers) throws IOException {
            jgen.writeStringField("name", value.getName());
            jgen.writeNumberField("age", value.getAge());
        }

    }

    public static class Deserializer extends JsonDeserializer<MyObject> {

        @Override
        public MyObject deserialize(JsonParser jsonParser, DeserializationContext ctxt)
                throws IOException, JsonProcessingException {
            ObjectCodec codec = jsonParser.getCodec();
            JsonNode tree = codec.readTree(jsonParser);
            String name = tree.get("name").textValue();
            int age = tree.get("age").intValue();
            return new MyObject(name, age);
        }

    }

}

ApplicationContext 内のすべての @JsonComponent Bean は、Jackson に自動的に登録されます。@JsonComponent は @Component でメタアノテーションが付けられているため、通常のコンポーネントスキャンルールが適用されます。

Spring Boot は、オブジェクトを直列化するときに標準 Jackson バージョンの有用な代替を提供する JsonObjectSerializer [GitHub] (英語) および JsonObjectDeserializer [GitHub] (英語) 基本クラスも提供します。詳細については、Javadoc の JsonObjectSerializer (Javadoc) および JsonObjectDeserializer (Javadoc) を参照してください。

上記の例は、JsonObjectSerializer/JsonObjectDeserializer を使用するように次のように書き直すことができます。

import java.io.IOException;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.ObjectCodec;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.SerializerProvider;

import org.springframework.boot.jackson.JsonComponent;
import org.springframework.boot.jackson.JsonObjectDeserializer;
import org.springframework.boot.jackson.JsonObjectSerializer;

@JsonComponent
public class MyJsonComponent {

    public static class Serializer extends JsonObjectSerializer<MyObject> {

        @Override
        protected void serializeObject(MyObject value, JsonGenerator jgen, SerializerProvider provider)
                throws IOException {
            jgen.writeStringField("name", value.getName());
            jgen.writeNumberField("age", value.getAge());
        }

    }

    public static class Deserializer extends JsonObjectDeserializer<MyObject> {

        @Override
        protected MyObject deserializeObject(JsonParser jsonParser, DeserializationContext context, ObjectCodec codec,
                JsonNode tree) throws IOException {
            String name = nullSafeValue(tree.get("name"), String.class);
            int age = nullSafeValue(tree.get("age"), Integer.class);
            return new MyObject(name, age);
        }

    }

}

1.1.4. MessageCodesResolver

Spring MVC には、バインディングエラーからエラーメッセージをレンダリングするためのエラーコードを生成するための戦略があります: MessageCodesResolverspring.mvc.message-codes-resolver-format プロパティ PREFIX_ERROR_CODE または POSTFIX_ERROR_CODE を設定すると、Spring Boot によって作成されます ( DefaultMessageCodesResolver.Format (Javadoc) の列挙を参照)。

1.1.5. 静的コンテンツ

デフォルトでは、Spring Boot は、クラスパス内の /static (または /public または /resources または /META-INF/resources) というディレクトリから、または ServletContext のルートから静的コンテンツを提供します。Spring MVC の ResourceHttpRequestHandler を使用しているため、独自の WebMvcConfigurer を追加して addResourceHandlers メソッドをオーバーライドすることで、その動作を変更できます。

スタンドアロン Web アプリケーションでは、コンテナーからのデフォルトサーブレットも有効になり、フォールバックとして機能し、Spring が処理しないことを決定した場合、ServletContext のルートからコンテンツを提供します。Spring は常に DispatcherServlet を介してリクエストを処理できるため、ほとんどの場合、これは起こりません(デフォルトの MVC 構成を変更しない限り)。

デフォルトでは、リソースは /** にマップされますが、spring.mvc.static-path-pattern プロパティでそれを調整できます。たとえば、すべてのリソースを /resources/** に再配置するには、次のようにします。

Properties
spring.mvc.static-path-pattern=/resources/**
Yaml
spring:
  mvc:
    static-path-pattern: "/resources/**"

spring.web.resources.static-locations プロパティを使用して静的リソースの場所をカスタマイズすることもできます(デフォルト値をディレクトリの場所のリストに置き換えます)。ルートサーブレットコンテキストパス "/" も、場所として自動的に追加されます。

前述の「標準」の静的リソースの場所に加えて、Webjars コンテンツ (英語) には特別なケースが作成されます。WebZZ 形式でパッケージ化されている場合、/webjars/** にパスを持つリソースは jar ファイルから提供されます。

アプリケーションが jar としてパッケージ化されている場合は、src/main/webapp ディレクトリを使用しないでください。このディレクトリは一般的な標準ですが、war パッケージでのみ機能し、jar を生成する場合、ほとんどのビルドツールでは暗黙のうちに無視されます。

Spring Boot は、Spring MVC が提供する高度なリソース処理機能もサポートし、静的リソースのキャッシュ無効化や Webjar のバージョンに依存しない URL の使用などのユースケースを可能にします。

Webjar にバージョンに依存しない URL を使用するには、webjars-locator-core 依存関係を追加します。次に、Webjar を宣言します。例として jQuery を使用して、"/webjars/jquery/jquery.min.js" を追加すると、"/webjars/jquery/x.y.z/jquery.min.js" が生成されます(x.y.z は Webjar バージョンです)。

JBoss を使用する場合、webjars-locator-core ではなく webjars-locator-jboss-vfs 依存関係を宣言する必要があります。それ以外の場合、すべての Webjar は 404 として解決されます。

キャッシュ無効化を使用するには、次の構成ですべての静的リソースのキャッシュ無効化ソリューションを構成し、URL に <link href="/css/spring-2a2d595e6ed9a0b24f027f2b63b134d6.css"/> などのコンテンツハッシュを効果的に追加します。

Properties
spring.web.resources.chain.strategy.content.enabled=true
spring.web.resources.chain.strategy.content.paths=/**
Yaml
spring:
  web:
    resources:
      chain:
        strategy:
          content:
            enabled: true
            paths: "/**"
リソースへのリンクは、Thymeleaf および FreeMarker 用に自動構成された ResourceUrlEncodingFilter のおかげで、実行時にテンプレートで書き換えられます。JSP を使用する場合は、このフィルターを手動で宣言する必要があります。現在、他のテンプレートエンジンは自動的にサポートされていませんが、カスタムテンプレートマクロ / ヘルパーおよび ResourceUrlProvider (Javadoc) を使用することができます。

JavaScript モジュールローダーなどを使用してリソースを動的にロードする場合、ファイルの名前を変更することはできません。そのため、他の戦略もサポートされており、組み合わせることができます。"fixed" 戦略では、次の例に示すように、ファイル名を変更せずに URL に静的バージョン文字列を追加します。

Properties
spring.web.resources.chain.strategy.content.enabled=true
spring.web.resources.chain.strategy.content.paths=/**
spring.web.resources.chain.strategy.fixed.enabled=true
spring.web.resources.chain.strategy.fixed.paths=/js/lib/
spring.web.resources.chain.strategy.fixed.version=v12
Yaml
spring:
  web:
    resources:
      chain:
        strategy:
          content:
            enabled: true
            paths: "/**"
          fixed:
            enabled: true
            paths: "/js/lib/"
            version: "v12"

この構成では、"/js/lib/" にある JavaScript モジュールは固定バージョン管理戦略("/v12/js/lib/mymodule.js")を使用しますが、他のリソースはコンテンツコンテンツ(<link href="/css/spring-2a2d595e6ed9a0b24f027f2b63b134d6.css"/>)を引き続き使用します。

サポートされるオプションの詳細については、WebProperties.Resources [GitHub] (英語) を参照してください。

この機能は、専用のブログ投稿 (英語) および Spring Framework のリファレンスドキュメントで詳細に説明されています。

1.1.6. ウェルカムページ

Spring Boot は、静的なウェルカムページとテンプレート化されたウェルカムページの両方をサポートしています。最初に、構成された静的コンテンツの場所で index.html ファイルを探します。見つからない場合は、index テンプレートを探します。どちらかが見つかった場合、アプリケーションのウェルカムページとして自動的に使用されます。

1.1.7. パスマッチングとコンテンツネゴシエーション

Spring MVC は、リクエストパスを調べて、アプリケーションで定義されたマッピング (たとえば、コントローラーメソッドの @GetMapping アノテーション) と照合することにより、受信 HTTP リクエストをハンドラーにマッピングできます。

Spring Boot は、デフォルトでサフィックスパターンマッチングを無効にすることを選択します。これは、"GET /projects/spring-boot.json" のようなリクエストが @GetMapping("/projects/spring-boot") マッピングにマッチングされないことを意味します。これは、Spring MVC アプリケーションのベストプラクティスと見なされています。この機能は、これまで、適切な "Accept" リクエストヘッダーを送信しなかった HTTP クライアントで主に役立ちました。正しいコンテンツ型をクライアントに送信する必要がありました。今日では、コンテントネゴシエーションの信頼性ははるかに高くなっています。

適切な "Accept" リクエストヘッダーを一貫して送信しない HTTP クライアントを処理する方法は他にもあります。サフィックスマッチングを使用する代わりに、クエリパラメーターを使用して、"GET /projects/spring-boot?format=json" などのリクエストが @GetMapping("/projects/spring-boot") にマップされるようにすることができます。

Properties
spring.mvc.contentnegotiation.favor-parameter=true
Yaml
spring:
  mvc:
    contentnegotiation:
      favor-parameter: true

または、別のパラメーター名を使用する場合:

Properties
spring.mvc.contentnegotiation.favor-parameter=true
spring.mvc.contentnegotiation.parameter-name=myparam
Yaml
spring:
  mvc:
    contentnegotiation:
      favor-parameter: true
      parameter-name: "myparam"

ほとんどの標準メディア型はすぐにサポートされますが、新しいメディア型を定義することもできます。

Properties
spring.mvc.contentnegotiation.media-types.markdown=text/markdown
Yaml
spring:
  mvc:
    contentnegotiation:
      media-types:
        markdown: "text/markdown"

サフィックスパターンマッチングは非推奨であり、将来のリリースでは削除される予定です。警告を理解していても、アプリケーションでサフィックスパターンマッチングを使用する場合は、次の構成が必要です。

Properties
spring.mvc.contentnegotiation.favor-path-extension=true
spring.mvc.pathmatch.use-suffix-pattern=true
Yaml
spring:
  mvc:
    contentnegotiation:
      favor-path-extension: true
    pathmatch:
      use-suffix-pattern: true

または、すべてのサフィックスパターンを開くよりも、登録済みのサフィックスパターンのみをサポートする方が安全です。

Properties
spring.mvc.contentnegotiation.favor-path-extension=true
spring.mvc.pathmatch.use-registered-suffix-pattern=true
Yaml
spring:
  mvc:
    contentnegotiation:
      favor-path-extension: true
    pathmatch:
      use-registered-suffix-pattern: true

Spring Framework 5.3 の時点で、Spring MVC は、リクエストパスをコントローラーハンドラーに一致させるためのいくつかの実装戦略をサポートしています。以前は AntPathMatcher 戦略のみをサポートしていましたが、現在は PathPatternParser も提供しています。Spring Boot は、新しい戦略を選択して選択するための構成プロパティを提供するようになりました。

Properties
spring.mvc.pathmatch.matching-strategy=path-pattern-parser
Yaml
spring:
  mvc:
    pathmatch:
      matching-strategy: "path-pattern-parser"

この新しい実装を検討する必要がある理由の詳細については、専用のブログ投稿 (英語) を参照してください。

PathPatternParser は最適化された実装ですが、一部のパスパターンバリアントの使用を制限し、サフィックスパターンマッチング(spring.mvc.pathmatch.use-suffix-patternspring.mvc.pathmatch.use-registered-suffix-pattern)またはサーブレットプレフィックスを使用した DispatcherServlet のマッピング(spring.mvc.servlet.path)と互換性がありません。

1.1.8. ConfigurableWebBindingInitializer

Spring MVC は WebBindingInitializer を使用して、特定のリクエストに対して WebDataBinder を初期化します。独自の ConfigurableWebBindingInitializer@Bean を作成すると、Spring Boot はそれを使用するように Spring MVC を自動的に構成します。

1.1.9. テンプレートエンジン

REST Web サービスだけでなく、Spring MVC を使用して動的 HTML コンテンツを提供することもできます。Spring MVC は、Thymeleaf、FreeMarker、JSP など、さまざまなテンプレートテクノロジをサポートしています。また、他の多くのテンプレートエンジンには、独自の Spring MVC 統合が含まれています。

Spring Boot には、次のテンプレートエンジンの自動構成サポートが含まれています。

可能であれば、JSP を避ける必要があります。組み込みサーブレットコンテナーで使用する場合、いくつかの既知の制限があります。

これらのテンプレートエンジンのいずれかを既定の構成で使用すると、src/main/resources/templates からテンプレートが自動的に選択されます。

アプリケーションの実行メソッドに応じて、IDE はクラスパスの順序を変える場合があります。IDE でメインメソッドからアプリケーションを実行すると、Maven または Gradle を使用して、またはパッケージ化された jar からアプリケーションを実行する場合とは異なる順序になります。これにより、Spring Boot が予期されたテンプレートを見つけられない可能性があります。この問題が発生した場合は、IDE でクラスパスを並べ替えて、モジュールのクラスとリソースを最初に配置できます。

1.1.10. エラー処理

デフォルトでは、Spring Boot はすべてのエラーを適切な方法で処理する /error マッピングを提供し、サーブレットコンテナーに「グローバル」エラーページとして登録されます。マシンクライアントの場合、エラー、HTTP ステータス、例外メッセージの詳細を含む JSON レスポンスを生成します。ブラウザークライアントの場合、同じデータを HTML 形式でレンダリングする「ホワイトラベル」エラービューがあります(カスタマイズするには、error に解決される View を追加します)。

デフォルトのエラー処理動作をカスタマイズする場合に設定できる server.error プロパティがいくつかあります。付録の “サーバープロパティ” セクションを参照してください。

デフォルトの動作を完全に置き換えるには、ErrorController を実装してその型の Bean 定義を登録するか、型 ErrorAttributes の Bean を追加して既存のメカニズムを使用しますが、内容を置き換えます。

BasicErrorController は、カスタム ErrorController の基本クラスとして使用できます。これは、新しいコンテンツ型のハンドラーを追加する場合に特に便利です(デフォルトでは、text/html を具体的に処理し、他のすべてにフォールバックを提供します)。これを行うには、BasicErrorController を継承し、produces 属性を持つ @RequestMapping を使用して public メソッドを追加し、新しい型の Bean を作成します。

次の例に示すように、@ControllerAdvice アノテーションが付けられたクラスを定義して、特定のコントローラーまたは例外型、あるいはその両方を返すように JSON ドキュメントをカスタマイズすることもできます。

import javax.servlet.RequestDispatcher;
import javax.servlet.http.HttpServletRequest;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;

@ControllerAdvice(basePackageClasses = SomeController.class)
public class MyControllerAdvice extends ResponseEntityExceptionHandler {

    @ResponseBody
    @ExceptionHandler(MyException.class)
    public ResponseEntity<?> handleControllerException(HttpServletRequest request, Throwable ex) {
        HttpStatus status = getStatus(request);
        return new ResponseEntity<>(new MyErrorBody(status.value(), ex.getMessage()), status);
    }

    private HttpStatus getStatus(HttpServletRequest request) {
        Integer code = (Integer) request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE);
        HttpStatus status = HttpStatus.resolve(code);
        return (status != null) ? status : HttpStatus.INTERNAL_SERVER_ERROR;
    }

}

上記の例では、YourException が SomeController と同じパッケージで定義されたコントローラーによってスローされた場合、ErrorAttributes 表現の代わりに CustomErrorType POJO の JSON 表現が使用されます。

場合によっては、コントローラーレベルで処理されたエラーは、メトリクスインフラストラクチャによって記録されません。アプリケーションは、処理された例外をリクエスト属性として設定することにより、そのような例外がリクエストメトリクスとともに記録されることを保証できます。

import javax.servlet.http.HttpServletRequest;

import org.springframework.boot.web.servlet.error.ErrorAttributes;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ExceptionHandler;

@Controller
public class MyController {

    @ExceptionHandler(CustomException.class)
    String handleCustomException(HttpServletRequest request, CustomException ex) {
        request.setAttribute(ErrorAttributes.ERROR_ATTRIBUTE, ex);
        return "errorView";
    }

}
カスタムエラーページ

特定のステータスコードのカスタム HTML エラーページを表示する場合は、/error ディレクトリにファイルを追加できます。エラーページは、静的 HTML(つまり、任意の静的リソースディレクトリに追加される)にすることも、テンプレートを使用して作成することもできます。ファイルの名前は、正確なステータスコードまたはシリーズマスクである必要があります。

例: 404 を静的 HTML ファイルにマップするには、ディレクトリ構造は次のようになります。

src/
 +- main/
     +- java/
     |   + <source code>
     +- resources/
         +- public/
             +- error/
             |   +- 404.html
             +- <other public assets>

FreeMarker テンプレートを使用してすべての 5xx エラーをマップするには、ディレクトリ構造は次のようになります。

src/
 +- main/
     +- java/
     |   + <source code>
     +- resources/
         +- templates/
             +- error/
             |   +- 5xx.ftlh
             +- <other templates>

より複雑なマッピングの場合、次の例に示すように、ErrorViewResolver インターフェースを実装する Bean を追加することもできます。

import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.springframework.boot.autoconfigure.web.servlet.error.ErrorViewResolver;
import org.springframework.http.HttpStatus;
import org.springframework.web.servlet.ModelAndView;

public class MyErrorViewResolver implements ErrorViewResolver {

    @Override
    public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status, Map<String, Object> model) {
        // Use the request or status to optionally return a ModelAndView
        if (status == HttpStatus.INSUFFICIENT_STORAGE) {
            // We could add custom model values here
            new ModelAndView("myview");
        }
        return null;
    }

}

@ExceptionHandler メソッド@ControllerAdvice などの通常の Spring MVC 機能も使用できます。その後、ErrorController は未処理の例外を取得します。

Spring MVC 外のエラーページのマッピング

Spring MVC を使用しないアプリケーションの場合、ErrorPageRegistrar インターフェースを使用して ErrorPages を直接登録できます。この抽象化は、基盤となる埋め込みサーブレットコンテナーと直接連携し、Spring MVC DispatcherServlet がなくても機能します。

import org.springframework.boot.web.server.ErrorPage;
import org.springframework.boot.web.server.ErrorPageRegistrar;
import org.springframework.boot.web.server.ErrorPageRegistry;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;

@Configuration(proxyBeanMethods = false)
public class MyErrorPagesConfiguration {

    @Bean
    public ErrorPageRegistrar errorPageRegistrar() {
        return this::registerErrorPages;
    }

    private void registerErrorPages(ErrorPageRegistry registry) {
        registry.addErrorPages(new ErrorPage(HttpStatus.BAD_REQUEST, "/400"));
    }

}
Filter によって処理されるパスで ErrorPage を登録する場合(Jersey や Wicket などの一部の非 SpringWeb フレームワークで一般的)、次に示すように、Filter を ERROR ディスパッチャーとして明示的に登録する必要があります。次の例:
import java.util.EnumSet;

import javax.servlet.DispatcherType;

import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration(proxyBeanMethods = false)
public class MyFilterConfiguration {

    @Bean
    public FilterRegistrationBean<MyFilter> myFilter() {
        FilterRegistrationBean<MyFilter> registration = new FilterRegistrationBean<>(new MyFilter());
        // ...
        registration.setDispatcherTypes(EnumSet.allOf(DispatcherType.class));
        return registration;
    }

}

デフォルトの FilterRegistrationBean には ERROR ディスパッチャー型が含まれていないことに注意してください。

war デプロイでのエラー処理

サーブレットコンテナーにデプロイされると、Spring Boot はエラーページフィルターを使用して、エラーステータスのあるリクエストを適切なエラーページに転送します。サーブレット仕様ではエラーページを登録するための API が提供されていないため、これが必要です。war ファイルをデプロイするコンテナーとアプリケーションが使用するテクノロジーによっては、追加の構成が必要になる場合があります。

エラーページフィルターは、レスポンスがまだコミットされていない場合にのみ、リクエストを正しいエラーページに転送できます。デフォルトでは、WebSphere アプリケーションサーバー 8.0 以降は、サーブレットのサービスメソッドが正常に完了すると、レスポンスをコミットします。com.ibm.ws.webcontainer.invokeFlushAfterService を false に設定して、この動作を無効にする必要があります。

Spring Security を使用していて、エラーページのプリンシパルにアクセスする場合は、Spring Security のフィルターがエラーディスパッチで呼び出されるように構成する必要があります。そのためには、spring.security.filter.dispatcher-types プロパティを async, error, forward, request に設定します。

1.1.11. CORS サポート

クロスオリジンリソース共有 [Mozilla] (CORS)は、ほとんどのブラウザー (英語) で実装されている W3C 仕様 (英語) であり、IFRAME や JSONP などの安全性の低いアプローチを使用する代わりに、どのようなクロスドメインリクエストを認可するかを柔軟に指定できます。

バージョン 4.2 以降、Spring MVC は CORS をサポートします。Spring Boot アプリケーションで @CrossOrigin (Javadoc) アノテーションを使用してコントローラーメソッド CORS 構成を使用する場合、特定の構成は必要ありません。グローバル CORS 設定は、次の例に示すように、カスタマイズされた addCorsMappings(CorsRegistry) メソッドで WebMvcConfigurer Bean を登録することで定義できます。

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration(proxyBeanMethods = false)
public class MyCorsConfiguration {

    @Bean
    public WebMvcConfigurer corsConfigurer() {
        return new WebMvcConfigurer() {

            @Override
            public void addCorsMappings(CorsRegistry registry) {
                registry.addMapping("/api/**");
            }

        };
    }

}

1.2. JAX-RS および Jersey

REST エンドポイントに JAX-RS プログラミングモデルを使用する場合は、Spring MVC の代わりに使用可能な実装の 1 つを使用できます。Jersey (英語) Apache CXF (英語) は、すぐに使用できます。CXF では、アプリケーションコンテキストで Servlet または Filter を @Bean として登録する必要があります。Jersey にはネイティブ Spring サポートがいくつかあるため、スターターとともに Spring Boot での自動構成サポートも提供します。

Jersey を開始するには、spring-boot-starter-jersey を依存関係として含めてから、次の例に示すように、すべてのエンドポイントを登録する型 ResourceConfig の @Bean が 1 つ必要です。

import org.glassfish.jersey.server.ResourceConfig;

import org.springframework.stereotype.Component;

@Component
public class MyJerseyConfig extends ResourceConfig {

    public MyJerseyConfig() {
        register(MyEndpoint.class);
    }

}
Jersey の実行可能アーカイブのスキャンのサポートはかなり制限されています。例: 実行可能な war ファイルを実行している場合、完全に実行可能な jar ファイルまたは WEB-INF/classes  で見つかったパッケージ内のエンドポイントをスキャンできません。この制限を回避するには、packages メソッドを使用せず、前の例に示すように、register メソッドを使用してエンドポイントを個別に登録する必要があります。

より高度なカスタマイズのために、ResourceConfigCustomizer を実装する Bean を任意の数だけ登録することもできます。

登録されたすべてのエンドポイントは、次の例に示すように、HTTP リソースアノテーション(@GET など)を含む @Components である必要があります。

import javax.ws.rs.GET;
import javax.ws.rs.Path;

import org.springframework.stereotype.Component;

@Component
@Path("/hello")
public class MyEndpoint {

    @GET
    public String message() {
        return "Hello";
    }

}

Endpoint は Spring @Component であるため、そのライフサイクルは Spring によって管理され、@Autowired アノテーションを使用して依存関係を注入し、@Value アノテーションを使用して外部構成を注入できます。デフォルトでは、Jersey サーブレットが登録され、/* にマップされます。@ApplicationPath を ResourceConfig に追加することにより、マッピングを変更できます。

デフォルトでは、Jersey は、jerseyServletRegistration という名前の ServletRegistrationBean 型の @Bean のサーブレットとして設定されます。デフォルトでは、サーブレットは遅延初期化されますが、spring.jersey.servlet.load-on-startup を設定することでその動作をカスタマイズできます。同じ名前で独自の Bean を作成することにより、その Bean を無効化またはオーバーライドできます。spring.jersey.type=filter を設定することにより、サーブレットの代わりにフィルターを使用することもできます(この場合、置換またはオーバーライドする @Bean は jerseyFilterRegistration です)。フィルターには @Order があり、spring.jersey.filter.order で設定できます。Jersey をフィルターとして使用する場合、Jersey によってインターセプトされないリクエストを処理するサーブレットが存在する必要があります。アプリケーションにそのようなサーブレットが含まれていない場合は、server.servlet.register-default-servlet を true に設定して、デフォルトのサーブレットを有効にすることをお勧めします。spring.jersey.init.* を使用してプロパティのマップを指定することにより、サーブレットとフィルターの両方の登録に init パラメーターを指定できます。

1.3. 組み込みサーブレットコンテナーのサポート

サーブレットアプリケーションの場合、Spring Boot には、組み込み Tomcat [Apache] (英語) Jetty (英語) Undertow [GitHub] (英語) サーバーのサポートが含まれています。ほとんどの開発者は、適切な「スターター」を使用して、完全に構成されたインスタンスを取得します。デフォルトでは、組み込みサーバーはポート 8080 で HTTP リクエストをリッスンします。

1.3.1. サーブレット、フィルター、リスナー

組み込みサーブレットコンテナーを使用する場合、Spring Bean を使用するか、サーブレットコンポーネントをスキャンすることにより、サーブレット、フィルター、すべてのリスナー(HttpSessionListener など)をサーブレット仕様から登録できます。

サーブレット、フィルター、リスナーを Spring Bean として登録する

Spring Bean である ServletFilter、サーブレット *Listener インスタンスはすべて、組み込みコンテナーに登録されます。これは、構成中に application.properties から値を参照する場合に特に便利です。

デフォルトでは、コンテキストに含まれるサーブレットが 1 つのみの場合、/ にマッピングされます。複数のサーブレット Bean の場合、Bean 名がパスプレフィックスとして使用されます。フィルターは /* にマップします。

規則ベースのマッピングに十分な柔軟性がない場合は、ServletRegistrationBeanFilterRegistrationBeanServletListenerRegistrationBean クラスを使用して完全に制御できます。

通常、フィルター Bean は順序付けされていないままにしておくのが安全です。特定の順序が必要な場合は、Filter に @Order のアノテーションを付けるか、Ordered を実装するようにする必要があります。Bean メソッドに @Order アノテーションを付けて、Filter の順序を構成することはできません。Filter クラスを変更して @Order を追加したり、Ordered を実装したりできない場合は、Filter の FilterRegistrationBean を定義し、setOrder(int) メソッドを使用して登録 Bean の順序を設定する必要があります。Ordered.HIGHEST_PRECEDENCE でリクエスト本文を読み取るフィルターを構成することは避けてください。これは、アプリケーションの文字エンコード構成に反する可能性があるためです。サーブレットフィルターがリクエストをラップする場合は、OrderedFilter.REQUEST_WRAPPER_FILTER_MAX_ORDER 以下の順序で設定する必要があります。

アプリケーション内のすべての Filter の順序を確認するには、web  ロギンググループlogging.level.web=debug)のデバッグレベルロギングを有効にします。登録されたフィルターの詳細(順序や URL パターンなど)は、起動時にログに記録されます。
Filter Bean はアプリケーションのライフサイクルの非常に早い段階で初期化されるため、登録するときは注意してください。他の Bean と相互作用する Filter を登録する必要がある場合は、代わりに DelegatingFilterProxyRegistrationBean (Javadoc) の使用を検討してください。

1.3.2. サーブレットコンテキストの初期化

組み込みサーブレットコンテナーは、サーブレット 3.0 + javax.servlet.ServletContainerInitializer インターフェースまたは Spring の org.springframework.web.WebApplicationInitializer インターフェースを直接実行しません。これは、war 内で実行するように設計されたサードパーティライブラリが Spring Boot アプリケーションを破壊するリスクを軽減することを目的とした意図的な設計上の決定です。

Spring Boot アプリケーションでサーブレットコンテキストの初期化を実行する必要がある場合は、org.springframework.boot.web.servlet.ServletContextInitializer インターフェースを実装する Bean を登録する必要があります。単一の onStartup メソッドは ServletContext へのアクセスを提供し、必要に応じて、既存の WebApplicationInitializer へのアダプターとして簡単に使用できます。

サーブレット、フィルター、リスナーのスキャン

組み込みコンテナーを使用する場合、@ServletComponentScan を使用すると、@WebServlet@WebFilter@WebListener アノテーションが付けられたクラスの自動登録を有効にできます。

@ServletComponentScan は、コンテナーの組み込み検出メカニズムが代わりに使用されるスタンドアロンコンテナーでは効果がありません。

1.3.3. ServletWebServerApplicationContext

内部的には、Spring Boot は組み込みサーブレットコンテナーのサポートに異なる型の ApplicationContext を使用します。ServletWebServerApplicationContext は、単一の ServletWebServerFactory Bean を検索することによってそれ自体をブートストラップする特殊な型の WebApplicationContext です。通常、TomcatServletWebServerFactoryJettyServletWebServerFactoryUndertowServletWebServerFactory は自動構成されています。

通常、これらの実装クラスを意識する必要はありません。ほとんどのアプリケーションは自動構成され、適切な ApplicationContext および ServletWebServerFactory がユーザーに代わって作成されます。

1.3.4. 埋め込みサーブレットコンテナーのカスタマイズ

一般的なサーブレットコンテナー設定は、Spring Environment プロパティを使用して構成できます。通常、application.properties または application.yaml ファイルでプロパティを定義します。

一般的なサーバー設定は次のとおりです。

  • ネットワーク設定: 受信 HTTP リクエストのリスンポート(server.port)、server.address にバインドするインターフェースアドレスなど。

  • セッション設定: セッションが永続的か(server.servlet.session.persistent)、セッションタイムアウト(server.servlet.session.timeout)、セッションデータの場所(server.servlet.session.store-dir)、セッション Cookie 構成(server.servlet.session.cookie.*)。

  • エラー管理: エラーページ(server.error.path)などの場所。

  • SSL

  • HTTP 圧縮

Spring Boot は、可能な限り共通の設定を公開しようとしますが、常に可能とは限りません。これらの場合、専用の名前空間はサーバー固有のカスタマイズを提供します(server.tomcat および server.undertow を参照)。たとえば、アクセスログは、組み込みサーブレットコンテナーの特定の機能を使用して設定できます。

完全なリストについては、ServerProperties [GitHub] (英語) クラスを参照してください。
SameSite クッキー

SameSite cookie 属性は、クロスサイトリクエストで Cookie を送信するかどうか、および送信する方法を制御するために Web ブラウザーで使用できます。この属性は、属性が欠落しているときに使用されるデフォルト値を変更し始めた最新の Web ブラウザーに特に関係があります。

セッション Cookie の SameSite 属性を変更する場合は、server.servlet.session.cookie.same-site プロパティを使用できます。このプロパティは、自動構成された Tomcat、Jetty、Undertow サーバーでサポートされています。また、Spring Session サーブレットベースの SessionRepository Bean を構成するためにも使用されます。

例: セッション Cookie に None の SameSite 属性を持たせたい場合は、application.properties または application.yaml ファイルに以下を追加できます。

Properties
server.servlet.session.cookie.same-site=none
Yaml
server:
  servlet:
    session:
      cookie:
        same-site: "none"

HttpServletResponse に追加された他の Cookie の SameSite 属性を変更する場合は、CookieSameSiteSupplier を使用できます。CookieSameSiteSupplier には Cookie が渡され、SameSite 値または null を返す場合があります。

特定の Cookie をすばやく照合するために使用できる便利なファクトリおよびフィルターメソッドがいくつかあります。例: 次の Bean を追加すると、正規表現 myapp.* と一致する名前のすべての Cookie に Lax の SameSite が自動的に適用されます。

import org.springframework.boot.web.servlet.server.CookieSameSiteSupplier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration(proxyBeanMethods = false)
public class MySameSiteConfiguration {

    @Bean
    public CookieSameSiteSupplier applicationCookieSameSiteSupplier() {
        return CookieSameSiteSupplier.ofLax().whenHasNameMatching("myapp.*");
    }

}
プログラムによるカスタマイズ

組み込みサーブレットコンテナーをプログラムで設定する必要がある場合は、WebServerFactoryCustomizer インターフェースを実装する Spring Bean を登録できます。WebServerFactoryCustomizer は、多数のカスタマイズ setter メソッドを含む ConfigurableServletWebServerFactory へのアクセスを提供します。次の例は、プログラムでポートを設定する方法を示しています。

import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory;
import org.springframework.stereotype.Component;

@Component
public class MyWebServerFactoryCustomizer implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> {

    @Override
    public void customize(ConfigurableServletWebServerFactory server) {
        server.setPort(9000);
    }

}

TomcatServletWebServerFactoryJettyServletWebServerFactoryUndertowServletWebServerFactory は ConfigurableServletWebServerFactory の専用バリアントであり、Tomcat、Jetty、Undertow 用にそれぞれ追加のカスタマイズ setter メソッドがあります。次の例は、Tomcat 固有の構成オプションへのアクセスを提供する TomcatServletWebServerFactory をカスタマイズする方法を示しています。

import java.time.Duration;

import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.stereotype.Component;

@Component
public class MyTomcatWebServerFactoryCustomizer implements WebServerFactoryCustomizer<TomcatServletWebServerFactory> {

    @Override
    public void customize(TomcatServletWebServerFactory server) {
        server.addConnectorCustomizers((connector) -> connector.setAsyncTimeout(Duration.ofSeconds(20).toMillis()));
    }

}
ConfigurableServletWebServerFactory を直接カスタマイズする

ServletWebServerFactory から拡張する必要があるより高度なユースケースの場合は、そのような型の Bean を自分で公開できます。

Setter は、多くの構成オプション用に提供されています。よりエキゾチックな何かをする必要がある場合、いくつかの protected メソッド「フック」も提供されます。詳細については、ソースコードのドキュメント (Javadoc) を参照してください。

自動構成されたカスタマイザーは引き続きカスタムファクトリに適用されるため、そのオプションは慎重に使用してください。

1.3.5. JSP の制限

組み込みサーブレットコンテナーを使用する(実行可能アーカイブとしてパッケージ化されている)Spring Boot アプリケーションを実行する場合、JSP サポートにはいくつかの制限があります。

  • Jetty と Tomcat では、war パッケージを使用すれば動作するはずです。実行可能な war は、java -jar で起動すると動作し、任意の標準コンテナーに配備することもできます。実行可能 jar を使用する場合、JSP はサポートされません。

  • Undertow は JSP をサポートしていません。

  • カスタム error.jsp ページを作成しても、エラー処理のデフォルトビューは上書きされません。代わりにカスタムエラーページを使用する必要があります。

2. リアクティブ Web アプリケーション

Spring Boot は、Spring Webflux の自動構成を提供することにより、リアクティブ Web アプリケーションの開発を簡素化します。

2.1. 「Spring WebFlux フレームワーク」

Spring WebFlux は、Spring Framework 5.0 で導入された新しいリアクティブ Web フレームワークです。Spring MVC とは異なり、サーブレット API を必要とせず、完全に非同期でノンブロッキングであり、Reactor プロジェクト (英語) を通じて Reactive Streams (英語) 仕様を実装します。

Spring WebFlux には、関数型とアノテーション型の 2 つのフレーバーがあります。次の例に示すように、アノテーションベースのものは Spring MVC モデルに非常に近いものです。

import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/users")
public class MyRestController {

    private final UserRepository userRepository;

    private final CustomerRepository customerRepository;

    public MyRestController(UserRepository userRepository, CustomerRepository customerRepository) {
        this.userRepository = userRepository;
        this.customerRepository = customerRepository;
    }

    @GetMapping("/{user}")
    public Mono<User> getUser(@PathVariable Long userId) {
        return this.userRepository.findById(userId);
    }

    @GetMapping("/{user}/customers")
    public Flux<Customer> getUserCustomers(@PathVariable Long userId) {
        return this.userRepository.findById(userId).flatMapMany(this.customerRepository::findByUser);
    }

    @DeleteMapping("/{user}")
    public void deleteUser(@PathVariable Long userId) {
        this.userRepository.deleteById(userId);
    }

}

次の例に示すように、関数型バリアントである "WebFlux.fn" は、ルーティング構成をリクエストの実際の処理から分離します。

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.server.RequestPredicate;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.ServerResponse;

import static org.springframework.web.reactive.function.server.RequestPredicates.DELETE;
import static org.springframework.web.reactive.function.server.RequestPredicates.GET;
import static org.springframework.web.reactive.function.server.RequestPredicates.accept;
import static org.springframework.web.reactive.function.server.RouterFunctions.route;

@Configuration(proxyBeanMethods = false)
public class MyRoutingConfiguration {

    private static final RequestPredicate ACCEPT_JSON = accept(MediaType.APPLICATION_JSON);

    @Bean
    public RouterFunction<ServerResponse> monoRouterFunction(MyUserHandler userHandler) {
        return route(
                GET("/{user}").and(ACCEPT_JSON), userHandler::getUser).andRoute(
                GET("/{user}/customers").and(ACCEPT_JSON), userHandler::getUserCustomers).andRoute(
                DELETE("/{user}").and(ACCEPT_JSON), userHandler::deleteUser);
    }

}
import reactor.core.publisher.Mono;

import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;

@Component
public class MyUserHandler {

    public Mono<ServerResponse> getUser(ServerRequest request) {
        ...
    }

    public Mono<ServerResponse> getUserCustomers(ServerRequest request) {
        ...
    }

    public Mono<ServerResponse> deleteUser(ServerRequest request) {
        ...
    }

}

WebFlux は Spring Framework の一部であり、詳細な情報はリファレンスドキュメントで入手できます。

RouterFunction Bean をいくつでも定義して、ルーターの定義をモジュール化できます。優先順位を適用する必要がある場合は、Bean をオーダーできます。

開始するには、spring-boot-starter-webflux モジュールをアプリケーションに追加します。

アプリケーションに spring-boot-starter-web モジュールと spring-boot-starter-webflux モジュールの両方を追加すると、WebFlux ではなく Spring Boot が Spring MVC を自動構成します。この動作が選択されたのは、多くの Spring 開発者が spring-boot-starter-webflux を Spring MVC アプリケーションに追加してリアクティブ WebClient を使用するためです。選択したアプリケーションの種類を SpringApplication.setWebApplicationType(WebApplicationType.REACTIVE) に設定することで、引き続き選択を強制できます。

次の例に示すように、関数型バリアントである "WebFlux.fn" は、ルーティング構成をリクエストの実際の処理から分離します。

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.server.RequestPredicate;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.ServerResponse;

import static org.springframework.web.reactive.function.server.RequestPredicates.DELETE;
import static org.springframework.web.reactive.function.server.RequestPredicates.GET;
import static org.springframework.web.reactive.function.server.RequestPredicates.accept;
import static org.springframework.web.reactive.function.server.RouterFunctions.route;

@Configuration(proxyBeanMethods = false)
public class MyRoutingConfiguration {

    private static final RequestPredicate ACCEPT_JSON = accept(MediaType.APPLICATION_JSON);

    @Bean
    public RouterFunction<ServerResponse> monoRouterFunction(MyUserHandler userHandler) {
        return route(
                GET("/{user}").and(ACCEPT_JSON), userHandler::getUser).andRoute(
                GET("/{user}/customers").and(ACCEPT_JSON), userHandler::getUserCustomers).andRoute(
                DELETE("/{user}").and(ACCEPT_JSON), userHandler::deleteUser);
    }

}
import reactor.core.publisher.Mono;

import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;

@Component
public class MyUserHandler {

    public Mono<ServerResponse> getUser(ServerRequest request) {
        ...
    }

    public Mono<ServerResponse> getUserCustomers(ServerRequest request) {
        ...
    }

    public Mono<ServerResponse> deleteUser(ServerRequest request) {
        ...
    }

}

WebFlux は Spring Framework の一部であり、詳細な情報はリファレンスドキュメントで入手できます。

RouterFunction Bean をいくつでも定義して、ルーターの定義をモジュール化できます。優先順位を適用する必要がある場合は、Bean をオーダーできます。

開始するには、spring-boot-starter-webflux モジュールをアプリケーションに追加します。

アプリケーションに spring-boot-starter-web モジュールと spring-boot-starter-webflux モジュールの両方を追加すると、WebFlux ではなく Spring Boot が Spring MVC を自動構成します。この動作が選択されたのは、多くの Spring 開発者が spring-boot-starter-webflux を Spring MVC アプリケーションに追加してリアクティブ WebClient を使用するためです。選択したアプリケーションの種類を SpringApplication.setWebApplicationType(WebApplicationType.REACTIVE) に設定することで、引き続き選択を強制できます。

2.1.1. Spring WebFlux 自動構成

Spring Boot は、ほとんどのアプリケーションで適切に機能する Spring WebFlux の自動構成を提供します。

自動構成により、Spring のデフォルトに加えて次の機能が追加されます。

  • HttpMessageReader および HttpMessageWriter インスタンスのコーデックの構成(このドキュメントで後述)。

  • WebJars のサポートを含む、静的リソースの提供のサポート(このドキュメントで後述)。

Spring Boot WebFlux 機能を保持し、さらに WebFlux の構成を追加する場合、@EnableWebFlux なしで型 WebFluxConfigurer の独自の @Configuration クラスを追加できます。

Spring WebFlux を完全に制御したい場合は、@EnableWebFlux アノテーションが付けられた独自の @Configuration を追加できます。

2.1.2. HttpMessageReaders および HttpMessageWriters を使用した HTTP コーデック

Spring WebFlux は、HttpMessageReader および HttpMessageWriter インターフェースを使用して、HTTP リクエストおよびレスポンスを変換します。これらは、クラスパスで使用可能なライブラリを調べることにより、実用的なデフォルトを持つように CodecConfigurer で構成されます。

Spring Boot は、コーデックの専用構成プロパティ spring.codec.* を提供します。また、CodecCustomizer インスタンスを使用して、さらにカスタマイズを適用します。例: spring.jackson.* 設定キーは Jackson コーデックに適用されます。

コーデックを追加またはカスタマイズする必要がある場合は、次の例に示すように、カスタム CodecCustomizer コンポーネントを作成できます。

import org.springframework.boot.web.codec.CodecCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.codec.ServerSentEventHttpMessageReader;

@Configuration(proxyBeanMethods = false)
public class MyCodecsConfiguration {

    @Bean
    public CodecCustomizer myCodecCustomizer() {
        return (configurer) -> {
            configurer.registerDefaults(false);
            configurer.customCodecs().register(new ServerSentEventHttpMessageReader());
            // ...
        };
    }

}

2.1.3. 静的コンテンツ

デフォルトでは、Spring Boot は、クラスパス内の /static (または /public または /resources または /META-INF/resources)と呼ばれるディレクトリから静的コンテンツを提供します。Spring WebFlux の ResourceWebHandler を使用するため、独自の WebFluxConfigurer を追加して addResourceHandlers メソッドをオーバーライドすることにより、その動作を変更できます。

デフォルトでは、リソースは /** にマップされますが、spring.webflux.static-path-pattern プロパティを設定することでそれを調整できます。たとえば、すべてのリソースを /resources/** に再配置するには、次のようにします。

Properties
spring.webflux.static-path-pattern=/resources/**
Yaml
spring:
  webflux:
    static-path-pattern: "/resources/**"

spring.web.resources.static-locations を使用して、静的リソースの場所をカスタマイズすることもできます。これにより、デフォルト値がディレクトリの場所のリストに置き換えられます。そうすると、デフォルトのウェルカムページ検出がカスタムの場所に切り替わります。起動時にいずれかの場所に index.html がある場合、アプリケーションのホームページです。

前述の「標準」の静的リソースの場所に加えて、Webjars コンテンツ (英語) には特別なケースが作成されます。WebZZ 形式でパッケージ化されている場合、/webjars/** にパスを持つリソースは jar ファイルから提供されます。

Spring WebFlux アプリケーションはサーブレット API に厳密に依存していないため、war ファイルとしてデプロイすることはできず、src/main/webapp ディレクトリを使用しません。

2.1.4. ウェルカムページ

Spring Boot は、静的なウェルカムページとテンプレート化されたウェルカムページの両方をサポートしています。最初に、構成された静的コンテンツの場所で index.html ファイルを探します。見つからない場合は、index テンプレートを探します。どちらかが見つかった場合、アプリケーションのウェルカムページとして自動的に使用されます。

2.1.5. テンプレートエンジン

REST Web サービスだけでなく、Spring WebFlux を使用して動的 HTML コンテンツを提供することもできます。Spring WebFlux は、Thymeleaf、FreeMarker、Mustache など、さまざまなテンプレートテクノロジーをサポートしています。

Spring Boot には、次のテンプレートエンジンの自動構成サポートが含まれています。

これらのテンプレートエンジンのいずれかを既定の構成で使用すると、src/main/resources/templates からテンプレートが自動的に選択されます。

2.1.6. エラー処理

Spring Boot は、すべてのエラーを適切な方法で処理する WebExceptionHandler を提供します。処理順序でのその位置は、WebFlux によって提供されるハンドラーの直前であり、最後に考慮されます。マシンクライアントの場合、エラー、HTTP ステータス、例外メッセージの詳細を含む JSON レスポンスを生成します。ブラウザークライアントには、同じデータを HTML 形式でレンダリングする「ホワイトラベル」エラーハンドラーがあります。また、独自の HTML テンプレートを提供してエラーを表示することもできます(次のセクションを参照)。

この機能をカスタマイズするための最初のステップは、多くの場合、既存のメカニズムを使用しますが、エラーの内容を置換または拡張することを伴います。そのために、型 ErrorAttributes の Bean を追加できます。

エラー処理の動作を変更するには、ErrorWebExceptionHandler を実装し、その型の Bean 定義を登録できます。ErrorWebExceptionHandler は非常に低レベルであるため、Spring Boot は次の例に示すように、WebFlux の関数方法でエラーを処理できる便利な AbstractErrorWebExceptionHandler も提供します。

import reactor.core.publisher.Mono;

import org.springframework.boot.autoconfigure.web.WebProperties.Resources;
import org.springframework.boot.autoconfigure.web.reactive.error.AbstractErrorWebExceptionHandler;
import org.springframework.boot.web.reactive.error.ErrorAttributes;
import org.springframework.context.ApplicationContext;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.RouterFunctions;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import org.springframework.web.reactive.function.server.ServerResponse.BodyBuilder;

@Component
public class MyErrorWebExceptionHandler extends AbstractErrorWebExceptionHandler {

    public MyErrorWebExceptionHandler(ErrorAttributes errorAttributes, Resources resources,
            ApplicationContext applicationContext) {
        super(errorAttributes, resources, applicationContext);
    }

    @Override
    protected RouterFunction<ServerResponse> getRoutingFunction(ErrorAttributes errorAttributes) {
        return RouterFunctions.route(this::acceptsXml, this::handleErrorAsXml);
    }

    private boolean acceptsXml(ServerRequest request) {
        return request.headers().accept().contains(MediaType.APPLICATION_XML);
    }

    public Mono<ServerResponse> handleErrorAsXml(ServerRequest request) {
        BodyBuilder builder = ServerResponse.status(HttpStatus.INTERNAL_SERVER_ERROR);
        // ... additional builder calls
        return builder.build();
    }

}

より完全な図を得るには、DefaultErrorWebExceptionHandler を直接サブクラス化し、特定のメソッドをオーバーライドすることもできます。

場合によっては、コントローラーまたはハンドラー関数レベルで処理されたエラーは、メトリクスインフラストラクチャによって記録されません。アプリケーションは、処理された例外をリクエスト属性として設定することにより、そのような例外がリクエストメトリクスとともに記録されることを保証できます。

import org.springframework.boot.web.reactive.error.ErrorAttributes;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.reactive.result.view.Rendering;
import org.springframework.web.server.ServerWebExchange;

@Controller
public class MyExceptionHandlingController {

    @GetMapping("/profile")
    public Rendering userProfile() {
        // ...
        throw new IllegalStateException();
    }

    @ExceptionHandler(IllegalStateException.class)
    public Rendering handleIllegalState(ServerWebExchange exchange, IllegalStateException exc) {
        exchange.getAttributes().putIfAbsent(ErrorAttributes.ERROR_ATTRIBUTE, exc);
        return Rendering.view("errorView").modelAttribute("message", exc.getMessage()).build();
    }

}
カスタムエラーページ

特定のステータスコードのカスタム HTML エラーページを表示する場合は、/error ディレクトリにファイルを追加できます。エラーページは、静的 HTML(つまり、任意の静的リソースディレクトリに追加される)にすることも、テンプレートで作成することもできます。ファイルの名前は、正確なステータスコードまたはシリーズマスクである必要があります。

例: 404 を静的 HTML ファイルにマップするには、ディレクトリ構造は次のようになります。

src/
 +- main/
     +- java/
     |   + <source code>
     +- resources/
         +- public/
             +- error/
             |   +- 404.html
             +- <other public assets>

Mustache テンプレートを使用してすべての 5xx エラーをマップするには、ディレクトリ構造は次のようになります。

src/
 +- main/
     +- java/
     |   + <source code>
     +- resources/
         +- templates/
             +- error/
             |   +- 5xx.mustache
             +- <other templates>

2.1.7. Web フィルター

Spring WebFlux は、HTTP リクエスト / レスポンス交換をフィルタリングするために実装できる WebFilter インターフェースを提供します。アプリケーションコンテキストで見つかった WebFilter Bean は、各交換のフィルタリングに自動的に使用されます。

フィルターの順序が重要な場合、Ordered を実装するか、@Order でアノテーションを付けることができます。Spring Boot の自動構成により、Web フィルターが構成される場合があります。その場合、次の表に示す順序が使用されます。

Web フィルター 順序

MetricsWebFilter

Ordered.HIGHEST_PRECEDENCE + 1

WebFilterChainProxy (Spring Security)

-100

HttpTraceWebFilter

Ordered.LOWEST_PRECEDENCE - 10

2.2. 埋め込み型リアクティブサーバーのサポート

Spring Boot には、Reactor Netty、Tomcat、Jetty、Undertow の組み込みリアクティブ Web サーバーのサポートが含まれています。ほとんどの開発者は、適切な「スターター」を使用して、完全に構成されたインスタンスを取得します。デフォルトでは、組み込みサーバーはポート 8080 で HTTP リクエストをリッスンします。

2.3. リアクティブサーバーリソースの構成

Reactor Netty または Jetty サーバーを自動構成する場合、Spring Boot は、サーバーインスタンスに HTTP リソースを提供する特定の Bean ReactorResourceFactory または JettyResourceFactory を作成します。

デフォルトでは、これらのリソースは、最適なパフォーマンスのために、Reactor Netty および Jetty クライアントとも共有されます。

  • 同じテクノロジーがサーバーとクライアントに使用されます

  • クライアントインスタンスは、Spring Boot によって自動構成された WebClient.Builder Bean を使用して構築されます

開発者は、カスタム ReactorResourceFactory または JettyResourceFactory Bean を提供することにより、Jetty および Reactor Netty のリソース構成をオーバーライドできます。これは、クライアントとサーバーの両方に適用されます。

WebClient ランタイムセクションでクライアント側のリソース構成について詳しく知ることができます。

3. グレースフルシャットダウン

グレースフルシャットダウンは、4 つの組み込み Web サーバー(Jetty、Reactor Netty、Tomcat、Undertow)のすべてと、リアクティブ Web アプリケーションとサーブレットベースの Web アプリケーションの両方でサポートされています。これは、アプリケーションコンテキストを閉じる一部として発生し、SmartLifecycle Bean を停止する最初のフェーズで実行されます。この停止処理は、既存のリクエストの補完は許可されるが、新しいリクエストは許可されない猶予期間を提供するタイムアウトを使用します。新しいリクエストが許可されない正確な方法は、使用されている Web サーバーによって異なります。Jetty、Reactor Netty、および Tomcat は、ネットワーク層でのリクエストの受け入れを停止します。Undertow はリクエストを受け入れますが、サービスを利用できない (503) レスポンスですぐにレスポンスします。

Tomcat による正常なシャットダウンには、Tomcat 9.0.33 以降が必要です。

グレースフルシャットダウンを有効にするには、次の例に示すように、server.shutdown プロパティを構成します。

Properties
server.shutdown=graceful
Yaml
server:
  shutdown: "graceful"

タイムアウト期間を構成するには、次の例に示すように、spring.lifecycle.timeout-per-shutdown-phase プロパティを構成します。

Properties
spring.lifecycle.timeout-per-shutdown-phase=20s
Yaml
spring:
  lifecycle:
    timeout-per-shutdown-phase: "20s"
IDE が適切な SIGTERM 信号を送信しない場合、IDE でグレースフルシャットダウンを使用すると正しく機能しない可能性があります。詳細については、IDE のドキュメントを参照してください。

4. Spring Security

Spring Security がクラスパスにある場合、Web アプリケーションはデフォルトで保護されます。Spring Boot は、Spring Security のコンテンツネゴシエーション戦略に基づいて、httpBasic と formLogin のどちらを使用するかを決定します。メソッドレベルのセキュリティを Web アプリケーションに追加するために、必要な設定で @EnableGlobalMethodSecurity を追加することもできます。追加情報は Spring Security リファレンスガイドにあります。

デフォルトの UserDetailsService には単一のユーザーがいます。ユーザー名は user であり、パスワードはランダムであり、次の例に示すように、アプリケーションの起動時に INFO レベルで出力されます。

Using generated security password: 78fa095d-3f4c-48b1-ad50-e24c31d5cf35
ロギング構成を微調整する場合は、org.springframework.boot.autoconfigure.security カテゴリが INFO -level メッセージを記録するように設定されていることを確認してください。それ以外の場合、デフォルトのパスワードは出力されません。

spring.security.user.name および spring.security.user.password を提供することにより、ユーザー名とパスワードを変更できます。

Web アプリケーションでデフォルトで取得する基本機能は次のとおりです。

  • UserDetailsService (または WebFlux アプリケーションの場合は ReactiveUserDetailsService)メモリ内ストアを持つ Bean と、生成されたパスワードを持つ単一のユーザー(ユーザーのプロパティについては SecurityProperties.User (Javadoc) を参照)。

  • アプリケーション全体(アクチュエーターがクラスパス上にある場合はアクチュエーターエンドポイントを含む)に対するフォームベースのログインまたは HTTP Basic セキュリティ(リクエストの Accept ヘッダーに依存)。

  • 認証イベントを公開するための DefaultAuthenticationEventPublisher

Bean を追加することにより、異なる AuthenticationEventPublisher を提供できます。

4.1. MVC セキュリティ

デフォルトのセキュリティ構成は、SecurityAutoConfiguration および UserDetailsServiceAutoConfiguration に実装されています。SecurityAutoConfiguration は Web セキュリティのために SpringBootWebSecurityConfiguration をインポートし、UserDetailsServiceAutoConfiguration は認証を構成します。これは Web 以外のアプリケーションにも関連します。デフォルトの Web アプリケーションセキュリティ構成を完全にオフにするか、OAuth2 クライアントやリソースサーバーなどの複数の Spring Security コンポーネントを組み合わせるには、型 SecurityFilterChain の Bean を追加します(そうしても、UserDetailsService 構成またはアクチュエーターのセキュリティは無効になりません)。

UserDetailsService 構成をオフにするために、型 UserDetailsServiceAuthenticationProviderAuthenticationManager の Bean を追加することもできます。

カスタム SecurityFilterChain または WebSecurityConfigurerAdapter Bean を追加することにより、アクセスルールをオーバーライドできます。Spring Boot は、アクチュエーターエンドポイントと静的リソースのアクセスルールを上書きするために使用できる便利なメソッドを提供します。EndpointRequest を使用して、management.endpoints.web.base-path プロパティに基づく RequestMatcher を作成できます。PathRequest を使用して、一般的に使用される場所にあるリソースの RequestMatcher を作成できます。

4.2. WebFlux セキュリティ

Spring MVC アプリケーションと同様に、spring-boot-starter-security 依存関係を追加することで WebFlux アプリケーションを保護できます。デフォルトのセキュリティ設定は ReactiveSecurityAutoConfiguration および UserDetailsServiceAutoConfiguration に実装されています。ReactiveSecurityAutoConfiguration は Web セキュリティのために WebFluxSecurityConfiguration をインポートし、UserDetailsServiceAutoConfiguration は認証を構成します。これは、非 Web アプリケーションにも関連します。デフォルトの Web アプリケーションセキュリティ設定を完全にオフにするには、型 WebFilterChainProxy の Bean を追加します (追加しても UserDetailsService 設定やアクチュエーターのセキュリティは無効になりません)。

UserDetailsService 構成もオフにするには、型 ReactiveUserDetailsService または ReactiveAuthenticationManager の Bean を追加できます。

カスタム SecurityWebFilterChain Bean を追加することにより、アクセスルールと、OAuth 2 クライアントやリソースサーバーなどの複数の Spring Security コンポーネントの使用を構成できます。Spring Boot は、アクチュエーターエンドポイントおよび静的リソースのアクセスルールをオーバーライドするために使用できる便利なメソッドを提供します。EndpointRequest を使用して、management.endpoints.web.base-path プロパティに基づく ServerWebExchangeMatcher を作成できます。

PathRequest を使用して、一般的に使用される場所のリソース用の ServerWebExchangeMatcher を作成できます。

例: 次のようなものを追加して、セキュリティ構成をカスタマイズできます。

import org.springframework.boot.autoconfigure.security.reactive.PathRequest;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.web.server.SecurityWebFilterChain;

@Configuration(proxyBeanMethods = false)
public class MyWebFluxSecurityConfiguration {

    @Bean
    public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
        http.authorizeExchange((spec) -> {
            spec.matchers(PathRequest.toStaticResources().atCommonLocations()).permitAll();
            spec.pathMatchers("/foo", "/bar").authenticated();
        });
        http.formLogin();
        return http.build();
    }

}

4.3. OAuth2

OAuth2 (英語) は、Spring でサポートされている広く使用されている認可フレームワークです。

4.3.1. クライアント

クラスパスに spring-security-oauth2-client がある場合、いくつかの自動構成を利用して OAuth2/Open ID Connect クライアントを設定できます。この構成では、OAuth2ClientProperties のプロパティを使用します。同じプロパティが、サーブレットアプリケーションとリアクティブアプリケーションの両方に適用されます。

次の例に示すように、spring.security.oauth2.client プレフィックスに複数の OAuth2 クライアントとプロバイダーを登録できます。

Properties
spring.security.oauth2.client.registration.my-client-1.client-id=abcd
spring.security.oauth2.client.registration.my-client-1.client-secret=password
spring.security.oauth2.client.registration.my-client-1.client-name=Client for user scope
spring.security.oauth2.client.registration.my-client-1.provider=my-oauth-provider
spring.security.oauth2.client.registration.my-client-1.scope=user
spring.security.oauth2.client.registration.my-client-1.redirect-uri=https://my-redirect-uri.com
spring.security.oauth2.client.registration.my-client-1.client-authentication-method=basic
spring.security.oauth2.client.registration.my-client-1.authorization-grant-type=authorization-code

spring.security.oauth2.client.registration.my-client-2.client-id=abcd
spring.security.oauth2.client.registration.my-client-2.client-secret=password
spring.security.oauth2.client.registration.my-client-2.client-name=Client for email scope
spring.security.oauth2.client.registration.my-client-2.provider=my-oauth-provider
spring.security.oauth2.client.registration.my-client-2.scope=email
spring.security.oauth2.client.registration.my-client-2.redirect-uri=https://my-redirect-uri.com
spring.security.oauth2.client.registration.my-client-2.client-authentication-method=basic
spring.security.oauth2.client.registration.my-client-2.authorization-grant-type=authorization_code

spring.security.oauth2.client.provider.my-oauth-provider.authorization-uri=https://my-auth-server/oauth/authorize
spring.security.oauth2.client.provider.my-oauth-provider.token-uri=https://my-auth-server/oauth/token
spring.security.oauth2.client.provider.my-oauth-provider.user-info-uri=https://my-auth-server/userinfo
spring.security.oauth2.client.provider.my-oauth-provider.user-info-authentication-method=header
spring.security.oauth2.client.provider.my-oauth-provider.jwk-set-uri=https://my-auth-server/token_keys
spring.security.oauth2.client.provider.my-oauth-provider.user-name-attribute=name
Yaml
spring:
  security:
    oauth2:
      client:
        registration:
          my-client-1:
            client-id: "abcd"
            client-secret: "password"
            client-name: "Client for user scope"
            provider: "my-oauth-provider"
            scope: "user"
            redirect-uri: "https://my-redirect-uri.com"
            client-authentication-method: "basic"
            authorization-grant-type: "authorization-code"

          my-client-2:
            client-id: "abcd"
            client-secret: "password"
            client-name: "Client for email scope"
            provider: "my-oauth-provider"
            scope: "email"
            redirect-uri: "https://my-redirect-uri.com"
            client-authentication-method: "basic"
            authorization-grant-type: "authorization_code"

        provider:
          my-oauth-provider:
            authorization-uri: "https://my-auth-server/oauth/authorize"
            token-uri: "https://my-auth-server/oauth/token"
            user-info-uri: "https://my-auth-server/userinfo"
            user-info-authentication-method: "header"
            jwk-set-uri: "https://my-auth-server/token_keys"
            user-name-attribute: "name"

OpenID Connect ディスカバリ (英語) をサポートする OpenIDConnect プロバイダーの場合、構成をさらに簡素化できます。プロバイダーは、発行者 ID としてアサートする URI である issuer-uri を使用して構成する必要があります。例: 提供された issuer-uri が "https://example.com" の場合、OpenID Provider Configuration Request は "https://example.com/.well-known/openid-configuration" になります。結果は OpenID Provider Configuration Response になると予想されます。次の例は、OpenID 接続プロバイダーを issuer-uri で構成する方法を示しています。

Properties
spring.security.oauth2.client.provider.oidc-provider.issuer-uri=https://dev-123456.oktapreview.com/oauth2/default/
Yaml
spring:
  security:
    oauth2:
      client:
        provider:
          oidc-provider:
            issuer-uri: "https://dev-123456.oktapreview.com/oauth2/default/"

デフォルトでは、Spring Security の OAuth2LoginAuthenticationFilter は /login/oauth2/code/* に一致する URL のみを処理します。redirect-uri をカスタマイズして別のパターンを使用する場合は、そのカスタムパターンを処理するための構成を提供する必要があります。例: サーブレットアプリケーションの場合、次のような独自の SecurityFilterChain を追加できます。

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.SecurityFilterChain;

@Configuration(proxyBeanMethods = false)
public class MyOAuthClientConfiguration {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http.authorizeRequests().anyRequest().authenticated();
        http.oauth2Login().redirectionEndpoint().baseUri("custom-callback");
        return http.build();
    }

}
Spring Boot は、クライアント登録の管理のために Spring Security によって使用される InMemoryOAuth2AuthorizedClientService を自動構成します。InMemoryOAuth2AuthorizedClientService の機能には制限があるため、開発環境でのみ使用することをお勧めします。本番環境では、JdbcOAuth2AuthorizedClientService を使用するか、OAuth2AuthorizedClientService の独自の実装を作成することを検討してください。
一般的なプロバイダーの OAuth2 クライアント登録

Google、Github、Facebook、Okta などの一般的な OAuth2 プロバイダーと OpenID プロバイダーについては、プロバイダーのデフォルトのセット (それぞれ googlegithubfacebookokta) が提供されています。

これらのプロバイダーをカスタマイズする必要がない場合は、provider 属性をデフォルトを推測する必要がある属性に設定できます。また、クライアント登録のキーがデフォルトでサポートされているプロバイダーと一致する場合、Spring Boot も同様に推測します。

つまり、次の例の 2 つの構成では Google プロバイダーを使用します。

Properties
spring.security.oauth2.client.registration.my-client.client-id=abcd
spring.security.oauth2.client.registration.my-client.client-secret=password
spring.security.oauth2.client.registration.my-client.provider=google
spring.security.oauth2.client.registration.google.client-id=abcd
spring.security.oauth2.client.registration.google.client-secret=password
Yaml
spring:
  security:
    oauth2:
      client:
        registration:
          my-client:
            client-id: "abcd"
            client-secret: "password"
            provider: "google"
          google:
            client-id: "abcd"
            client-secret: "password"

4.3.2. リソースサーバー

クラスパスに spring-security-oauth2-resource-server がある場合、Spring Boot は OAuth2 リソースサーバーをセットアップできます。JWT 構成の場合、次の例に示すように、JWK セット URI または OIDC 発行者 URI を指定する必要があります。

Properties
spring.security.oauth2.resourceserver.jwt.jwk-set-uri=https://example.com/oauth2/default/v1/keys
Yaml
spring:
  security:
    oauth2:
      resourceserver:
        jwt:
          jwk-set-uri: "https://example.com/oauth2/default/v1/keys"
Properties
spring.security.oauth2.resourceserver.jwt.issuer-uri=https://dev-123456.oktapreview.com/oauth2/default/
Yaml
spring:
  security:
    oauth2:
      resourceserver:
        jwt:
          issuer-uri: "https://dev-123456.oktapreview.com/oauth2/default/"
認可サーバーが JWK Set URI をサポートしていない場合、JWT の署名の検証に使用される公開鍵でリソースサーバーを構成できます。これは、spring.security.oauth2.resourceserver.jwt.public-key-location プロパティを使用して実行できます。値は、PEM エンコードされた x509 形式の公開キーを含むファイルを指す必要があります。

同じプロパティは、サーブレットアプリケーションとリアクティブアプリケーションの両方に適用できます。

または、サーブレットアプリケーション用に独自の JwtDecoder Bean を定義するか、リアクティブアプリケーション用に ReactiveJwtDecoder を定義できます。

JWT の代わりに Opaque トークンが使用されている場合は、次のプロパティを構成して、イントロスペクションを通じてトークンを検証できます。

Properties
spring.security.oauth2.resourceserver.opaquetoken.introspection-uri=https://example.com/check-token
spring.security.oauth2.resourceserver.opaquetoken.client-id=my-client-id
spring.security.oauth2.resourceserver.opaquetoken.client-secret=my-client-secret
Yaml
spring:
  security:
    oauth2:
      resourceserver:
        opaquetoken:
          introspection-uri: "https://example.com/check-token"
          client-id: "my-client-id"
          client-secret: "my-client-secret"

繰り返しますが、同じプロパティがサーブレットとリアクティブの両方のアプリケーションに適用可能です。

または、サーブレットアプリケーション用に独自の OpaqueTokenIntrospector Bean を定義するか、リアクティブアプリケーション用に ReactiveOpaqueTokenIntrospector を定義できます。

4.3.3. 認可サーバー

現在、Spring Security は OAuth 2.0 認可サーバーの実装をサポートしていません。ただし、この機能は Spring Security OAuth プロジェクトから利用でき、最終的には Spring Security に完全に置き換えられます。それまでは、spring-security-oauth2-autoconfigure モジュールを使用して、OAuth 2.0 認可サーバーを簡単にセットアップできます。手順については、ドキュメントを参照してください。

4.4. SAML 2.0

4.4.1. 証明書利用者

クラスパスに spring-security-saml2-service-provider がある場合は、自動構成を利用して SAML 2.0 依存パーティをセットアップできます。この構成では、Saml2RelyingPartyProperties のプロパティを使用します。

証明書利用者登録は、ID プロバイダー IDP とサービスプロバイダー SP の間のペア構成を表します。次の例に示すように、spring.security.saml2.relyingparty プレフィックスに複数の証明書利用者を登録できます。

Properties
spring.security.saml2.relyingparty.registration.my-relying-party1.signing.credentials[0].private-key-location=path-to-private-key
spring.security.saml2.relyingparty.registration.my-relying-party1.signing.credentials[0].certificate-location=path-to-certificate
spring.security.saml2.relyingparty.registration.my-relying-party1.decryption.credentials[0].private-key-location=path-to-private-key
spring.security.saml2.relyingparty.registration.my-relying-party1.decryption.credentials[0].certificate-location=path-to-certificate
spring.security.saml2.relyingparty.registration.my-relying-party1.identityprovider.verification.credentials[0].certificate-location=path-to-verification-cert
spring.security.saml2.relyingparty.registration.my-relying-party1.identityprovider.entity-id=remote-idp-entity-id1
spring.security.saml2.relyingparty.registration.my-relying-party1.identityprovider.sso-url=https://remoteidp1.sso.url

spring.security.saml2.relyingparty.registration.my-relying-party2.signing.credentials[0].private-key-location=path-to-private-key
spring.security.saml2.relyingparty.registration.my-relying-party2.signing.credentials[0].certificate-location=path-to-certificate
spring.security.saml2.relyingparty.registration.my-relying-party2.decryption.credentials[0].private-key-location=path-to-private-key
spring.security.saml2.relyingparty.registration.my-relying-party2.decryption.credentials[0].certificate-location=path-to-certificate
spring.security.saml2.relyingparty.registration.my-relying-party2.identityprovider.verification.credentials[0].certificate-location=path-to-other-verification-cert
spring.security.saml2.relyingparty.registration.my-relying-party2.identityprovider.entity-id=remote-idp-entity-id2
spring.security.saml2.relyingparty.registration.my-relying-party2.identityprovider.sso-url=https://remoteidp2.sso.url
Yaml
spring:
  security:
    saml2:
      relyingparty:
        registration:
          my-relying-party1:
            signing:
              credentials:
              - private-key-location: "path-to-private-key"
                certificate-location: "path-to-certificate"
            decryption:
              credentials:
              - private-key-location: "path-to-private-key"
                certificate-location: "path-to-certificate"
            identityprovider:
              verification:
                credentials:
                - certificate-location: "path-to-verification-cert"
              entity-id: "remote-idp-entity-id1"
              sso-url: "https://remoteidp1.sso.url"

          my-relying-party2:
            signing:
              credentials:
              - private-key-location: "path-to-private-key"
                certificate-location: "path-to-certificate"
            decryption:
              credentials:
              - private-key-location: "path-to-private-key"
                certificate-location: "path-to-certificate"
            identityprovider:
              verification:
                credentials:
                - certificate-location: "path-to-other-verification-cert"
              entity-id: "remote-idp-entity-id2"
              sso-url: "https://remoteidp2.sso.url"

5. Spring Session

Spring Boot は、さまざまなデータストアに Spring Session 自動構成を提供します。サーブレット Web アプリケーションを構築する場合、次のストアを自動構成できます。

  • JDBC

  • Redis

  • Hazelcast

  • MongoDB

サーブレットの自動構成により、@Enable*HttpSession を使用する必要がなくなります。

リアクティブ Web アプリケーションを構築する場合、次のストアを自動構成できます。

  • Redis

  • MongoDB

リアクティブ自動構成は、@Enable*WebSession を使用する必要性を置き換えます。

単一の Spring Session モジュールがクラスパスに存在する場合、Spring Boot はそのストア実装を自動的に使用します。実装が複数ある場合は、セッションの保存に使用する StoreType [GitHub] (英語) を選択する必要があります。たとえば、JDBC をバックエンドストアとして使用するには、次のようにアプリケーションを構成できます。

Properties
spring.session.store-type=jdbc
Yaml
spring:
  session:
    store-type: "jdbc"
store-type を none に設定することにより、Spring Session を無効にできます。

各ストアには特定の追加設定があります。たとえば、次の例に示すように、JDBC ストアのテーブルの名前をカスタマイズできます。

Properties
spring.session.jdbc.table-name=SESSIONS
Yaml
spring:
  session:
    jdbc:
      table-name: "SESSIONS"

セッションのタイムアウトを設定するには、spring.session.timeout プロパティを使用できます。そのプロパティがサーブレット Web アプリケーションで設定されていない場合、自動構成は server.servlet.session.timeout の値にフォールバックします。

@Enable*HttpSession (サーブレット)または @Enable*WebSession (リアクティブ)を使用して、Spring Session の構成を制御できます。これにより、自動構成がバックオフします。Spring Session は、前述の構成プロパティではなく、アノテーションの属性を使用して構成できます。

6. Spring HATEOAS

ハイパーメディアを利用する RESTful API を開発する場合、Spring Boot は、ほとんどのアプリケーションで適切に機能する Spring HATEOAS の自動構成を提供します。自動構成は、@EnableHypermediaSupport を使用する必要性を置き換え、ハイパーメディアベースのアプリケーションの構築を容易にするために多数の Bean を登録します。これには、LinkDiscoverers (クライアント側サポート用)や、レスポンスを目的の表現に正しくマーシャリングするように構成された ObjectMapper が含まれます。ObjectMapper は、さまざまな spring.jackson.* プロパティを設定するか、存在する場合は Jackson2ObjectMapperBuilder Bean によってカスタマイズされます。

@EnableHypermediaSupport を使用して、Spring HATEOAS の構成を制御できます。これを行うと、前述の ObjectMapper のカスタマイズが無効になることに注意してください。

spring-boot-starter-hateoas は Spring MVC に固有であり、Spring WebFlux と組み合わせないでください。Spring HATEOAS を Spring WebFlux と一緒に使用するために、spring-boot-starter-webflux とともに org.springframework.hateoas:spring-hateoas への直接依存関係を追加できます。

7. 次のステップ

これで、Spring Boot を使用して Web アプリケーションを開発する方法を十分に理解できたはずです。次のいくつかのセクションでは、Spring Boot がさまざまなデータテクノロジーメッセージングシステム、およびその他の IO 機能とどのように統合されるかについて説明します。アプリケーションのニーズに基づいて、これらのいずれかを選択できます。