# 万维网
Spring 启动非常适合于 Web 应用程序开发。你可以使用嵌入式 Tomcat、 Jetty、 Undertow 或 Netty 创建一个自包含的 HTTP 服务器。大多数 Web 应用程序使用spring-boot-starter-web
模块来快速启动和运行。你还可以选择使用spring-boot-starter-webflux
模块构建反应性 Web 应用程序。
如果你还没有开发 Spring 启动 Web 应用程序,那么可以按照 开始 部分中的“Hello World!”示例进行操作。
# 1. Servlet Web 应用程序
如果你想构建基于 Servlet 的 Web 应用程序,可以利用 Spring Boot 的自动配置来实现 Spring MVC 或 Jersey。
# 1.1.“ Spring Web MVC 框架”
Spring Web MVC framework (opens new window)(通常被称为“ Spring MVC”)是一个丰富的“模型视图控制器”Web 框架。 Spring MVC 允许你创建特殊的@Controller
或@RestController
bean 来处理传入的 HTTP 请求。通过使用@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);
}
}
“WebMVC.FN”是一种功能变体,它将路由配置与请求的实际处理分离开来,如下例所示:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.web.servlet.function.RequestPredicate;
import org.springframework.web.servlet.function.RouterFunction;
import org.springframework.web.servlet.function.ServerResponse;
import static org.springframework.web.servlet.function.RequestPredicates.accept;
import static org.springframework.web.servlet.function.RouterFunctions.route;
@Configuration(proxyBeanMethods = false)
public class MyRoutingConfiguration {
private static final RequestPredicate ACCEPT_JSON = accept(MediaType.APPLICATION_JSON);
@Bean
public RouterFunction<ServerResponse> routerFunction(MyUserHandler userHandler) {
return route()
.GET("/{user}", ACCEPT_JSON, userHandler::getUser)
.GET("/{user}/customers", ACCEPT_JSON, userHandler::getUserCustomers)
.DELETE("/{user}", ACCEPT_JSON, userHandler::deleteUser)
.build();
}
}
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.function.ServerRequest;
import org.springframework.web.servlet.function.ServerResponse;
@Component
public class MyUserHandler {
public ServerResponse getUser(ServerRequest request) {
...
return ServerResponse.ok().build();
}
public ServerResponse getUserCustomers(ServerRequest request) {
...
return ServerResponse.ok().build();
}
public ServerResponse deleteUser(ServerRequest request) {
...
return ServerResponse.ok().build();
}
}
Spring MVC 是核心 Spring 框架的一部分,详细信息可在参考文献 (opens new window)中获得。在spring.io/guides (opens new window)上也有几个涵盖 Spring MVC 的指南。
你可以定义任意多的RouterFunction bean 来模块化路由器的定义。如果需要应用优先级,可以订购bean。 |
---|
# 1.1.1. Spring MVC 自动配置
Spring Boot 为 Spring MVC 提供了自动配置,该配置在大多数应用程序中都能很好地工作。
自动配置在 Spring 的默认值之上添加了以下功能:
包含
ContentNegotiatingViewResolver
和BeanNameViewResolver
bean。对服务静态资源的支持,包括对 WebJAR 的支持(覆盖在本文的后面部分)。
自动注册
Converter
、GenericConverter
和Formatter
bean。支持
HttpMessageConverters
(覆盖在本文的后面部分)。自动注册
MessageCodesResolver
(覆盖在本文的后面部分)。静态
index.html
支持。自动使用
ConfigurableWebBindingInitializer
Bean(覆盖在本文的后面部分)。
如果你希望保留那些 Spring 引导 MVC 自定义并使更多MVC 定制 (opens new window)(拦截器、格式化程序、视图控制器和其他功能),则可以添加你自己的@Configuration
类型的WebMvcConfigurer
但没有@EnableWebMvc
类。
如果希望提供RequestMappingHandlerMapping
、RequestMappingHandlerAdapter
或ExceptionHandlerExceptionResolver
的自定义实例,并且仍然保持 Spring 引导 MVC 自定义,则可以声明类型WebMvcRegistrations
的 Bean 并使用它来提供这些组件的自定义实例。
如果你想完全控制 Spring MVC,你可以添加你自己的@Configuration
,并使用@EnableWebMvc
进行注释,或者也可以添加你自己的@Configuration
-注释DelegatingWebMvcConfiguration
,如@EnableWebMvc
的 Javadoc 中所描述的。
Spring MVC 使用不同的ConversionService 来转换来自application.properties 或application.yaml 文件的值,表示 Period ,Duration 和DataSize 转换器不可用,并且@DurationUnit 和@DataSizeUnit 注释将被忽略。如果你想定制 Spring MVC 使用的 ConversionService ,你可以提供带有WebMvcConfigurer Bean 的addFormatters 方法。从该方法你可以注册任何你喜欢的转换器,或者你可以将其委托给 ApplicationConversionService 上可用的静态方法。 |
---|
# 1.1.2.HtpMessageConverters
Spring MVC 使用HttpMessageConverter
接口来转换 HTTP 请求和响应。合理的默认值是开箱即用的。例如,对象可以自动转换为 JSON(通过使用 Jackson 库)或 XML(通过使用 JacksonXML 扩展(如果可用的话),或者通过使用 JAXB(如果 JacksonXML 扩展不可用的话)。默认情况下,字符串编码为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 中注册 (opens new window),但是 Spring Boot 提供了一种替代的@JsonComponent
注释,使得直接注册 Spring bean 更加容易。
你可以直接在JsonSerializer
、JsonDeserializer
或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 还提供了[JsonObjectSerializer
](https://github.com/ Spring-projects/ Spring-boot/tree/v2.6.4/ Spring-boot-project/ Spring-boot/SRC/main/java/org/springframework/Jackson/jsonobjectserializer.java.java)和[<gtr="149"/>](https://github.com/[ Spring-projects/ Spring-boot/tree/v2.6.4/ Spring- Spring-boot-project-project/[参见[JsonObjectSerializer
](https://DOCS. Spring.io/ Spring-boot/DOCS/2.6.4/api/org/springframework/boot/Jackson/jsonobjectserializer.html)和[JsonObjectDeserializer
](https://DOCS. Spring.io/ Spring-boot/DOCS/2.6.4/api/org/springframework/boot/Jackson/jsonobjeserializer.html)中的 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.Messenger EcodesResolver
Spring MVC 具有用于生成用于从绑定错误呈现错误消息的错误代码的策略:。如果你设置spring.mvc.message-codes-resolver-format
属性PREFIX_ERROR_CODE
或POSTFIX_ERROR_CODE
, Spring boot 将为你创建一个(参见[DefaultMessageCodesResolver.Format
]中的枚举(https://DOCS. Spring.io/ Spring-framework/DOCS/5.3.16/javadoc-api/org/springframework/defalidation/ressMessagecover.format.html))。
# 1.1.5.静态内容
默认情况下, Spring 引导从 Classpath 中的一个名为/static
(或/public
或/resources
或/META-INF/resources
)的目录或从ServletContext
的根目录提供静态内容。它使用 Spring MVC 中的ResourceHttpRequestHandler
,这样你就可以通过添加自己的WebMvcConfigurer
并重写addResourceHandlers
方法来修改该行为。
在独立的 Web 应用程序中,来自容器的默认 Servlet 也被启用,并充当后备,如果 Spring 决定不处理它,则从ServletContext
的根目录中提供内容。在大多数情况下,这种情况不会发生(除非你修改了默认的 MVC 配置),因为 Spring 始终可以通过DispatcherServlet
处理请求。
默认情况下,资源映射在/**
上,但你可以使用spring.mvc.static-path-pattern
属性对其进行调优。例如,将所有资源重新定位到/resources/**
可以通过以下方式实现:
属性
spring.mvc.static-path-pattern=/resources/**
Yaml
spring:
mvc:
static-path-pattern: "/resources/**"
你还可以通过使用spring.web.resources.static-locations
属性(用目录位置列表替换默认值)来定制静态资源位置。根 Servlet 上下文路径"/"
也会自动添加为位置。
除了前面提到的“标准”静态资源位置之外,Webjars 内容 (opens new window)还有一个特殊情况。路径在/webjars/**
中的任何资源,如果以 WebJARS 格式打包,都将从 jar 文件中得到服务。
如果你的应用程序被打包为 jar,请不要使用src/main/webapp 目录。虽然这个目录是一个通用的标准,但是它在 WAR 打包中是只有的,如果你生成 jar,大多数构建工具都会默默地忽略它。 |
---|
Spring 启动还支持由 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-jboss-vfs 依赖项,而不是webjars-locator-core 。否则,所有 WebJAR 解析为 404 。 |
---|
为了使用缓存破坏,以下配置为所有静态资源配置了一个缓存破坏解决方案,有效地在 URL 中添加了内容散列,例如<link href="/css/spring-2a2d595e6ed9a0b24f027f2b63b134d6.css"/>
:
属性
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: "/**"
资源的链接在运行时在模板中被重写,由于ResourceUrlEncodingFilter 是为 ThymeLeaf 和 Freemarker 自动配置的。在使用 JSP 时,你应该手动声明此过滤器。 其他模板引擎目前不能自动支持,但可以与自定义模板宏/助手一起使用[ ResourceUrlProvider ](https://DOCS. Spring.io/ Spring-framework/DOCS/5.3.16/javc-api/org/spramework/spramework/ Servlet/resource/resourceurceurlprovider.html)。 |
---|
例如,当使用 JavaScript 模块加载程序动态加载资源时,不能选择重命名文件。这就是为什么其他策略也得到了支持,并且可以结合在一起。“固定”策略在不更改文件名的情况下在 URL 中添加静态版本字符串,如下例所示:
属性
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"
),而其他资源仍然使用 Content One(<link href="/css/spring-2a2d595e6ed9a0b24f027f2b63b134d6.css"/>
)。
参见[Web属性.Resources
](https://github.com/ Spring-projects/ Spring-boot/tree/v2.6.4/ Spring-boot-project/ Spring-boot-autofigure/SRC/main/java/org/springframework/boot/autofigure/web/webproperties.java)以获得更多支持的选项。
这个特性已经在一个专用的blog post (opens new window)和 Spring 框架的参考文献 (opens new window)中进行了详细的描述。 |
---|
# 1.1.6.欢迎页面
Spring 启动支持静态和模板化欢迎页面。它首先在配置的静态内容位置中查找index.html
文件。如果没有找到一个,那么它将查找index
模板。如果找到其中之一,它将自动用作应用程序的欢迎页面。
# 1.1.7.路径匹配和内容协商
Spring MVC 可以通过查看请求路径并将其与你的应用程序中定义的映射(例如,控制器方法上的@GetMapping
注释)匹配,将传入的 HTTP 请求映射到处理程序。
Spring 引导默认情况下选择禁用后缀模式匹配,这意味着像"GET /projects/spring-boot.json"
这样的请求将不会匹配到@GetMapping("/projects/spring-boot")
映射。这被认为是best practice for Spring MVC applications (opens new window)。这个特性在过去主要用于 HTTP 客户机,因为 HTTP 客户机没有发送正确的“接受”请求头;我们需要确保向客户机发送正确的内容类型。如今,内容协商更可靠了。
还有其他方法来处理不能始终发送正确的“接受”请求头的 HTTP 客户机。与使用后缀匹配不同,我们可以使用一个查询参数来确保像"GET /projects/spring-boot?format=json"
这样的请求将被映射到@GetMapping("/projects/spring-boot")
:
属性
spring.mvc.contentnegotiation.favor-parameter=true
Yaml
spring:
mvc:
contentnegotiation:
favor-parameter: true
或者,如果你更喜欢使用不同的参数名:
属性
spring.mvc.contentnegotiation.favor-parameter=true
spring.mvc.contentnegotiation.parameter-name=myparam
Yaml
spring:
mvc:
contentnegotiation:
favor-parameter: true
parameter-name: "myparam"
大多数标准媒体类型都支持开箱即用,但你也可以定义新的媒体类型:
属性
spring.mvc.contentnegotiation.media-types.markdown=text/markdown
Yaml
spring:
mvc:
contentnegotiation:
media-types:
markdown: "text/markdown"
后缀模式匹配不受欢迎,将在未来的版本中删除。如果你理解这些警告,并且仍然希望你的应用程序使用后缀模式匹配,则需要进行以下配置:
属性
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
或者,与其打开所有后缀模式,不如只支持已注册的后缀模式更安全:
属性
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 Framework5.3 中, Spring MVC 支持几种实现策略,用于将请求路径匹配到控制器处理程序。它以前只支持AntPathMatcher
策略,但现在也提供PathPatternParser
策略。 Spring 现在的启动提供了一种可在新策略中选择和 OPT 的配置属性:
属性
spring.mvc.pathmatch.matching-strategy=path-pattern-parser
Yaml
spring:
mvc:
pathmatch:
matching-strategy: "path-pattern-parser"
有关为什么应该考虑这个新实现的更多详细信息,请参见专门的博客文章 (opens new window)。
PathPatternParser 是一个优化的实现,但限制了一些路径模式变体 (opens new window)的使用,并且与后缀模式匹配(spring.mvc.pathmatch.use-suffix-pattern ,spring.mvc.pathmatch.use-registered-suffix-pattern )或映射带有 Servlet 前缀(spring.mvc.servlet.path )的DispatcherServlet 不兼容。 |
---|
# 1.1.8.配置 WebBindingInitializer
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 启动包括对以下模板引擎的自动配置支持:
如果可能的话,应该避免使用 JSP。 在使用嵌入式 Servlet 容器时,有几个已知限制。 |
---|
当你使用这些模板引擎中的一个具有默认配置时,你的模板将自动从src/main/resources/templates
中提取。
取决于你如何运行你的应用程序,你的 IDE 可能会以不同的方式对 Classpath 进行排序。 在 IDE 中从其主方法运行你的应用程序会导致与使用 Maven 或 Gradle 运行你的应用程序时不同的排序。或者从其打包的 jar。 这可能会导致 Spring 引导无法找到预期的模板。 如果你有这个问题,你可以重新排序 IDE 中的 Classpath,以便首先放置模块的类和资源。 |
---|
# 1.1.10.错误处理
默认情况下, Spring Boot 提供了一个/error
映射,该映射以合理的方式处理所有错误,并将其注册为 Servlet 容器中的“全局”错误页。对于机器客户机,它生成一个 JSON 响应,其中包含错误、HTTP 状态和异常消息的详细信息。对于浏览器客户机,有一个“WhiteLabel”错误视图,该视图以 HTML 格式呈现相同的数据(要对其进行自定义,请添加一个View
,将其解析为error
)。
如果你想定制默认的错误处理行为,那么可以设置许多server.error
属性。参见附录中的“服务器属性”部分。
要完全替换缺省行为,可以实现ErrorController
并注册该类型的 Bean 定义,或者添加ErrorAttributes
类型的 Bean 来使用现有机制但替换内容。
BasicErrorController 可以用作自定义ErrorController 的基类,如果你想为新的内容类型添加一个处理程序,这一点特别有用(默认情况是专门处理 text/html ,并为其他所有内容提供一个后备),这样做,扩展 BasicErrorController ,添加一个带有@RequestMapping 属性的公共方法,并创建新类型的 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
相同的包中定义的控制器抛出,则使用CustomErrorType
POJO 的 JSON 表示,而不是ErrorAttributes
表示。
在某些情况下,在控制器级别处理的错误不会被度量基础设施记录。通过将已处理的异常设置为请求属性,应用程序可以确保将此类异常与请求度量一起记录:
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;
}
}
你也可以使用常规的 Spring MVC 特性,例如[@ExceptionHandler
方法](https://DOCS. Spring.io/ Spring-Framework/DOCS/5.3.16/Reference/html/web.html#MVC-ExceptionHandlers)和[@ControllerAdvice
](https://DOCS. Spring.io/ Spring-framework/DOCS/5.3.16/reference/html/web.html-controller-mover-advc-advice)。然后,ErrorController
将获取任何未处理的异常。
# 映射 Spring MVC#### 之外的错误页面
对于不使用 Spring MVC 的应用程序,可以使用ErrorPageRegistrar
接口直接注册ErrorPages
。这个抽象可以直接与底层嵌入的 Servlet 容器一起工作,并且即使你没有 Spring MVC也可以工作。
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"));
}
}
如果你注册了一个ErrorPage ,其路径最终由Filter 处理(这在一些非 Spring Web 框架中很常见,例如 Jersey 和 Wicket),那么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
Dispatcher 类型。
# WAR 部署中的错误处理
当部署到 Servlet 容器时, Spring 引导使用其错误页过滤器将具有错误状态的请求转发到适当的错误页。这是必要的,因为 Servlet 规范不提供用于注册错误页的 API。根据部署 WAR 文件的容器和应用程序使用的技术,可能需要进行一些额外的配置。
如果响应尚未提交,则错误页筛选器只能将请求转发到正确的错误页。默认情况下,WebSphere Application Server8.0 及以后版本在成功完成 Servlet 的服务方法后提交响应。你应该通过将com.ibm.ws.webcontainer.invokeFlushAfterService
设置为false
来禁用此行为。
如果你正在使用 Spring 安全,并且希望访问错误页中的主体,则必须配置 Spring Security 的筛选器,以便在错误分派时调用该筛选器。为此,将spring.security.filter.dispatcher-types
属性设置为async, error, forward, request
。
# 1.1.11.CORS 支持
跨源资源共享 (opens new window)是一个W3C 规范 (opens new window)由大多数浏览器 (opens new window)实现的W3C 规范 (opens new window),它允许你以一种灵活的方式指定授权哪种类型的跨域请求,而不是使用一些安全性较低、功能较弱的方法,例如 IFRAME 或 JSONP。
在版本 4.2 中, Spring MVC支持 CORS (opens new window)。在 Spring 引导应用程序中使用控制器方法 CORS 配置 (opens new window)与[@CrossOrigin
](https://DOCS. Spring.io/ Spring-framework/DOCS/5.3.16/javadoc-api/org/springframework/web/bind/annotation/crossorigin.html)注释不需要任何特定的配置。全局 CORS 配置 (opens new window)可以通过使用定制的WebMvcConfigurer
[gt116]注册一个addCorsMappings(CorsRegistry)
方法来定义,如下例所示:
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 和泽西岛
如果你更喜欢用于 REST 端点的 JAX-RS 编程模型,那么你可以使用其中一个可用的实现,而不是 Spring MVC。和在开箱即用的情况下工作得很好。CXF 要求你在应用程序上下文中将其Servlet
或Filter
注册为@Bean
。Jersey 有一些本机的 Spring 支持,因此我们还在 Spring 启动中为它提供了自动配置支持,以及一个启动器。
要开始使用 Jersey,将spring-boot-starter-jersey
作为依赖项,然后需要一个类型为ResourceConfig
的@Bean
,在其中注册所有端点,如以下示例所示:
import org.glassfish.jersey.server.ResourceConfig;
import org.springframework.stereotype.Component;
@Component
public class MyJerseyConfig extends ResourceConfig {
public MyJerseyConfig() {
register(MyEndpoint.class);
}
}
Jersey 对扫描可执行文档的支持相当有限,例如,当运行可执行 WAR 文件时,它不能扫描在fully executable jar file或WEB-INF/classes 中找到的包中的端点。为了避免这种限制,不应使用 packages 方法,端点应该使用register 方法单独注册,如前面的示例所示。 |
---|
对于更高级的定制,你还可以注册任意数量的 bean 来实现ResourceConfigCustomizer
。
所有注册的端点都应该是@Components
,带有 HTTP 资源注释(@GET
等),如下例所示:
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
注释来注入外部配置。默认情况下,泽西 Servlet 被注册并映射到/*
。可以通过将@ApplicationPath
添加到ResourceConfig
来更改映射。
默认情况下,在ServletRegistrationBean
类型的@Bean
中将泽西设置为 Servlet,名称为jerseyServletRegistration
。默认情况下, Servlet 是惰性初始化的,但是你可以通过设置spring.jersey.servlet.load-on-startup
来定制这种行为。你可以通过创建自己的同名应用程序来禁用或重写该 Bean。还可以通过设置spring.jersey.type=filter
(在这种情况下,要替换或覆盖的@Bean
是jerseyFilterRegistration
)来使用过滤器而不是 Servlet。过滤器有一个@Order
,你可以用spring.jersey.filter.order
设置它。当使用 Jersey 作为过滤器时,必须有一个 Servlet 来处理未被 Jersey 截获的任何请求。如果你的应用程序不包含这样的 Servlet,那么你可能希望通过将server.servlet.register-default-servlet
设置为true
来启用缺省 Servlet。 Servlet 和过滤器注册都可以通过使用spring.jersey.init.*
来指定属性的映射来给定 init 参数。
# 1.3.嵌入式 Servlet 容器支持
对于 Servlet 应用程序, Spring 引导包括对嵌入式Tomcat (opens new window)、Jetty (opens new window)和Undertow (opens new window)服务器的支持。大多数开发人员使用适当的“启动器”来获得完全配置的实例。默认情况下,嵌入式服务器侦听端口8080
上的 HTTP 请求。
# 1.3.1.servlet、过滤器和侦听器
当使用嵌入式 Servlet 容器时,可以通过使用 Spring bean 或通过扫描 Servlet 组件来注册来自 Servlet 规范的 servlet、过滤器和所有侦听器(例如HttpSessionListener
)。
# 将 servlet、过滤器和侦听器注册为 Spring bean#####
任何Servlet
,Filter
,或 Servlet *Listener
实例中的 Spring Bean 是在嵌入式容器中注册的。如果你希望在配置过程中引用来自application.properties
的值,那么这将非常方便。
默认情况下,如果上下文只包含一个 Servlet,则将其映射到/
。在多个 Servlet bean 的情况下, Bean 名称被用作路径前缀。过滤器映射到/*
。
如果基于约定的映射不够灵活,则可以使用ServletRegistrationBean
、FilterRegistrationBean
和ServletListenerRegistrationBean
类来完成控制。
通常情况下,保持过滤豆的无序状态是安全的。如果需要特定的顺序,你应该用@Order
注释Filter
,或者使其实现Ordered
。不能通过用@Order
注释其 Bean 方法来配置Filter
的顺序。如果不能将Filter
类更改为添加@Order
或实现Ordered
,则必须为Filter
定义一个FilterRegistrationBean
,并使用setOrder(int)
方法设置注册 Bean 的顺序。避免配置读取请求主体Ordered.HIGHEST_PRECEDENCE
的筛选器,因为它可能与应用程序的字符编码配置不一致。如果 Servlet 过滤器包装了该请求,则应将其配置为小于或等于OrderedFilter.REQUEST_WRAPPER_FILTER_MAX_ORDER
的顺序。
要查看应用程序中每个Filter 的顺序,请启用web 伐木组(logging.level.web=debug )的调试级别日志。将在启动时记录已注册过滤器的详细信息,包括它们的顺序和 URL 模式。 |
---|
注册Filter bean 时要小心,因为它们在应用程序生命周期的很早就被初始化了。如果需要注册一个与其他 bean 交互的 Filter ,请考虑使用一个[DelegatingFilterProxyRegistrationBean ](https://DOCS. Spring.io/ Spring-boot/DOCS/2.6.4/api/org/springframework/boot/web/ Servlet/delegatingfilterbean.html)来代替。 |
---|
# 1.3.2. Servlet 上下文初始化
嵌入式 Servlet 容器不直接执行 Servlet 3.0+javax.servlet.ServletContainerInitializer
接口或 Spring 的org.springframework.web.WebApplicationInitializer
接口。这是一种有意的设计决策,旨在降低设计用于在 WAR 内部运行的第三方库可能会破坏 Spring 引导应用程序的风险。
如果需要在 Spring 引导应用程序中执行 Servlet 上下文初始化,则应该注册一个实现org.springframework.boot.web.servlet.ServletContextInitializer
接口的 Bean。单一的onStartup
方法提供了对ServletContext
的访问,并且如果需要,可以很容易地用作现有WebApplicationInitializer
的适配器。
# 扫描 servlet、过滤器和侦听器
当使用嵌入式容器时,使用@WebServlet
、@WebFilter
和@WebListener
注释的类的自动注册可以通过使用@ServletComponentScan
来启用。
@ServletComponentScan 在独立容器中没有任何作用,相反,在独立容器中使用的是容器的内置发现机制。 |
---|
# 1.3.3.ServletWebServerApplicationContext
在引擎盖下, Spring 引导使用不同类型的ApplicationContext
用于嵌入式 Servlet 容器支持。ServletWebServerApplicationContext
是WebApplicationContext
的一种特殊类型,它通过搜索单个ServletWebServerFactory
Bean 来引导自身。通常TomcatServletWebServerFactory
、JettyServletWebServerFactory
或UndertowServletWebServerFactory
已被自动配置。
你通常不需要了解这些实现类。 大多数应用程序都是自动配置的,并且适当的 ApplicationContext 和ServletWebServerFactory 是代表你创建的。 |
---|
# 1.3.4.自定义嵌入式 Servlet 容器
可通过使用 Spring Environment
属性来配置公共 Servlet 容器设置。通常,你会在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
)等等。
Spring 引导尝试尽可能多地公开公共设置,但这并不总是可能的。对于这些情况,专用的名称空间提供了特定于服务器的定制(参见server.tomcat
和server.undertow
)。例如,访问日志可以配置具有嵌入式 Servlet 容器的特定特征。
参见[Server属性 ](https://github.com/ Spring-projects/ Spring-boot/tree/v2.6.4/ Spring-boot-project/ Spring-boot-autofigure/SRC/main/java/org/springframework/boot/autofigure/web/serverproperties.java)类的完整列表。 |
---|
# Samesite 饼干
Web 浏览器可以使用SameSite
cookie 属性来控制跨站点请求中是否以及如何提交 cookie。该属性对于现代 Web 浏览器特别相关,因为它们已经开始更改缺省属性时使用的默认值。
如果要更改会话 cookie 的SameSite
属性,可以使用server.servlet.session.cookie.same-site
属性。自动配置的 Tomcat、 Jetty 和 Undertow 服务器支持此属性。它还用于配置基于SessionRepository
bean 的 Spring 会话 Servlet。
例如,如果你希望你的会话 cookie 具有SameSite
的None
属性,则可以将以下内容添加到你的application.properties
或application.yaml
文件中:
属性
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 应用SameSite
的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.*");
}
}
# 程序化定制
如果需要以编程方式配置你的嵌入式 Servlet 容器,则可以注册一个实现WebServerFactoryCustomizer
接口的 Spring Bean。WebServerFactoryCustomizer
提供对ConfigurableServletWebServerFactory
的访问,其中包括许多自定义 setter 方法。下面的示例以编程方式显示了端口的设置:
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);
}
}
TomcatServletWebServerFactory
、JettyServletWebServerFactory
和UndertowServletWebServerFactory
是ConfigurableServletWebServerFactory
的专用变体,它们分别具有用于 Tomcat、 Jetty 和 Undertow 的附加自定义 setter 方法。下面的示例展示了如何定制TomcatServletWebServerFactory
,从而提供对 Tomcat 特定配置选项的访问:
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()));
}
}
# 直接自定义配置 WebServerFactory#####
对于需要从ServletWebServerFactory
扩展的更高级的用例,你可以自己公开这种类型的 Bean。
为许多配置选项提供了设置器。如果你需要做一些更奇特的事情,还提供了几种受保护的方法“挂钩”。有关详细信息,请参见源代码文档 (opens new window)。
自动配置的 Customizer 仍然应用于你的自定义工厂,因此请小心使用该选项。 |
---|
# 1.3.5.JSP 限制
当运行使用嵌入式 Servlet 容器(并打包为可执行归档文件)的 Spring 引导应用程序时,JSP 支持中存在一些限制。
对于 Jetty 和 Tomcat,如果使用战争包装,它应该可以工作。当使用
java -jar
启动时,可执行 WAR 将工作,并且还可以部署到任何标准容器。在使用可执行文件 jar 时,不支持 JSP。Undertow 不支持 JSP。
# 2. 反应式 Web 应用程序
Spring 启动通过为 Spring WebFlux 提供自动配置简化了反应性 Web 应用程序的开发。
# 2.1.“ Spring WebFlux 框架”
Spring WebFlux 是在 Spring Framework5.0 中引入的新的反应性 Web 框架。与 Spring MVC 不同,它不需要 Servlet API,是完全异步和非阻塞的,并且通过反应堆项目 (opens new window)实现了反应流 (opens new window)规范。
Spring WebFlux 有两种风格:基于功能的和基于注释的。基于注释的模型非常接近 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.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}", ACCEPT_JSON, userHandler::getUser)
.GET("/{user}/customers", ACCEPT_JSON, userHandler::getUserCustomers)
.DELETE("/{user}", ACCEPT_JSON, userHandler::deleteUser)
.build();
}
}
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 框架的一部分,其参考文献 (opens new window)中提供了详细信息。
你可以定义任意多的RouterFunction bean 来模块化路由器的定义。如果需要应用优先级,可以订购bean。 |
---|
要开始,将spring-boot-starter-webflux
模块添加到应用程序中。
在应用程序中同时添加spring-boot-starter-web 和spring-boot-starter-webflux 模块会导致 Spring 引导自动配置 Spring MVC,不是 WebFlux.之所以选择这种行为,是因为许多 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.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}", ACCEPT_JSON, userHandler::getUser)
.GET("/{user}/customers", ACCEPT_JSON, userHandler::getUserCustomers)
.DELETE("/{user}", ACCEPT_JSON, userHandler::deleteUser)
.build();
}
}
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 框架的一部分,其参考文献 (opens new window)中提供了详细信息。
你可以定义任意多的RouterFunction bean 来模块化路由器的定义。如果需要应用优先级,可以订购bean。 |
---|
要开始,将spring-boot-starter-webflux
模块添加到应用程序中。
在应用程序中同时添加spring-boot-starter-web 和spring-boot-starter-webflux 模块将导致 Spring 引导自动配置 Spring MVC,不是 WebFlux.之所以选择这种行为,是因为许多 Spring 开发人员将 spring-boot-starter-webflux 添加到他们的 Spring MVC 应用程序中,以使用反应式WebClient 。你仍然可以通过将选择的应用程序类型设置为 SpringApplication.setWebApplicationType(WebApplicationType.REACTIVE) 来强制执行你的选择。 |
---|
# 2.1.1. Spring WebFlux 自动配置
Spring 启动为 Spring WebFlux 提供了自动配置,该配置在大多数应用程序中都能很好地工作。
自动配置在 Spring 的默认值之上添加了以下功能:
如果你希望保留 Spring boot WebFlux 特性并且希望添加额外的WebFlux 配置 (opens new window),则可以添加你自己的@Configuration
类型的类别WebFluxConfigurer
但是没有@EnableWebFlux
。
如果你想完全控制 Spring WebFlux,那么可以添加你自己的@Configuration
,并使用@EnableWebFlux
进行注释。
# 2.1.2.带有 HttpMessageReaders 和 HttpMessageWriters 的 HTTP 编解码器
Spring WebFlux 使用HttpMessageReader
和HttpMessageWriter
接口来转换 HTTP 请求和响应。通过查看 Classpath 中可用的库,将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());
// ...
};
}
}
你还可以利用Boot 的自定义 JSON 序列化器和反序列化器。
# 2.1.3.静态内容
默认情况下, Spring 引导从 Classpath 中的一个名为/static
(或/public
或/resources
或/META-INF/resources
)的目录中提供静态内容。它使用 Spring WebFlux 中的ResourceWebHandler
,以便你可以通过添加自己的WebFluxConfigurer
并重写addResourceHandlers
方法来修改该行为。
默认情况下,资源映射在/**
上,但是你可以通过设置spring.webflux.static-path-pattern
属性对其进行调优。例如,将所有资源重新定位到/resources/**
可以通过以下方式实现:
属性
spring.webflux.static-path-pattern=/resources/**
Yaml
spring:
webflux:
static-path-pattern: "/resources/**"
你还可以使用spring.web.resources.static-locations
自定义静态资源位置。这样做可以用目录位置列表替换默认值。如果你这样做,默认的欢迎页面检测将切换到你的自定义位置。因此,如果在启动时的任何位置都有index.html
,那么它就是应用程序的主页。
除了前面列出的“标准”静态资源位置之外,Webjars 内容 (opens new window)还有一个特殊情况。路径在/webjars/**
中的任何资源,如果以 WebJARS 格式打包,都将从 jar 文件中得到服务。
Spring WebFlux 应用程序不严格依赖于 Servlet API,因此它们不能作为 WAR 文件部署,也不使用src/main/webapp 目录。 |
---|
# 2.1.4.欢迎页面
Spring 启动支持静态和模板化欢迎页面。它首先在配置的静态内容位置中查找index.html
文件。如果没有找到一个,那么它将查找index
模板。如果找到其中之一,它将自动用作应用程序的欢迎页面。
# 2.1.5.模板引擎
与 REST Web 服务一样,你也可以使用 Spring WebFlux 来服务动态 HTML 内容。 Spring WebFlux 支持各种模板化技术,包括胸腺叶、自由标记和胡子。
Spring 启动包括对以下模板引擎的自动配置支持:
当你使用这些模板引擎中的一个具有默认配置时,你的模板将从src/main/resources/templates
中自动拾取。
# 2.1.6.错误处理
Spring Boot 提供了一个WebExceptionHandler
,它以合理的方式处理所有错误。它在处理顺序中的位置紧随 WebFlux 提供的处理程序之前,后者被认为是最后一个处理程序。对于机器客户机,它生成一个 JSON 响应,其中包含错误、HTTP 状态和异常消息的详细信息。对于浏览器客户端,有一个“WhiteLabel”错误处理程序,它以 HTML 格式呈现相同的数据。你也可以提供自己的 HTML 模板来显示错误(参见下一节)。
定制此功能的第一步通常涉及使用现有机制,但要替换或增加错误内容。为此,你可以添加类型ErrorAttributes
的 Bean。
要更改错误处理行为,可以实现ErrorWebExceptionHandler
并注册该类型的 Bean 定义。因为ErrorWebExceptionHandler
是相当低级的, Spring boot 还提供了一个方便的AbstractErrorWebExceptionHandler
,让你以 WebFlux 功能方式处理错误,如以下示例所示:
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.网页过滤器
Spring WebFlux 提供了一个WebFilter
接口,其可以实现为过滤 HTTP 请求-响应交换。WebFilter
在应用程序上下文中找到的 bean 将自动用于过滤每个交换。
在过滤器的顺序很重要的地方,它们可以实现Ordered
或用@Order
进行注释。 Spring 引导自动配置可以为你配置 Web 过滤器。当它这样做时,将使用下表所示的订单:
网页过滤器 | Order |
---|---|
MetricsWebFilter | Ordered.HIGHEST_PRECEDENCE + 1 |
WebFilterChainProxy ( Spring 证券) | -100 |
HttpTraceWebFilter | Ordered.LOWEST_PRECEDENCE - 10 |
# 2.2.嵌入式反应式服务器支持
Spring 引导包括对以下嵌入式反应性 Web 服务器的支持:Reactor Netty、 Tomcat、 Jetty 和 Undertow。大多数开发人员使用适当的“启动器”来获得完全配置的实例。默认情况下,嵌入式服务器监听端口 8080 上的 HTTP 请求。
# 2.3.反应式服务器资源配置
当自动配置反应堆网络或 Jetty 服务器时, Spring 引导将创建特定的 bean,这些 bean 将向服务器实例提供 HTTP 资源:ReactorResourceFactory
或JettyResourceFactory
。
默认情况下,这些资源也将与 Reactor Netty 和 Jetty 客户端共享,以获得最佳性能,给定:
同样的技术也用于服务器和客户端。
客户机实例是使用由 Spring 引导自动配置的
WebClient.Builder
Bean 来构建的。
开发人员可以通过提供自定义ReactorResourceFactory
或JettyResourceFactory
Bean 来覆盖 Jetty 和反应器网络的资源配置-这将同时应用于客户机和服务器。
你可以在WebClient 运行时部分中了解有关客户端资源配置的更多信息。
# 3. 优雅的关机
所有四个嵌入式 Web 服务器( Jetty、Reactor Netty、 Tomcat 和 Undertow)以及反应式和基于 Servlet 的 Web 应用程序都支持优雅的关闭。它作为关闭应用程序上下文的一部分出现,并在停止SmartLifecycle
bean 的最早阶段执行。此停止处理使用超时,超时提供了一个宽限期,在此期间将允许完成现有的请求,但不允许新的请求。不允许新请求的确切方式取决于所使用的 Web 服务器。 Jetty、反应堆网络和 Tomcat 将在网络层停止接受请求。 Undertow 将接受请求,但立即响应具有服务不可用的(503)响应。
使用 Tomcat 的优雅关机需要 Tomcat 9.0.33 或更高版本。 |
---|
要启用优雅的关机,请配置server.shutdown
属性,如以下示例所示:
属性
server.shutdown=graceful
Yaml
server:
shutdown: "graceful"
要配置超时周期,请配置spring.lifecycle.timeout-per-shutdown-phase
属性,如以下示例所示:
属性
spring.lifecycle.timeout-per-shutdown-phase=20s
Yaml
spring:
lifecycle:
timeout-per-shutdown-phase: "20s"
如果 IDE 没有发送适当的SIGTERM 信号,那么在 IDE 中使用 Graceful Shutdown 可能无法正常工作。有关更多详细信息,请参见 IDE 的文档。 |
---|
# 4. Spring Security
如果Spring Security (opens new window)在 Classpath 上,那么默认情况下 Web 应用程序是安全的。 Spring 启动依赖于 Spring 安全性的内容协商策略来确定是否使用httpBasic
或formLogin
。要向 Web 应用程序添加方法级安全性,还可以添加带有所需设置的@EnableGlobalMethodSecurity
。其他信息可以在Spring Security Reference Guide (opens new window)中找到。
默认的UserDetailsService
只有一个用户。用户名是user
,密码是随机的,并在应用程序启动时在信息级别打印,如以下示例所示:
Using generated security password: 78fa095d-3f4c-48b1-ad50-e24c31d5cf35
如果你微调了日志配置,请确保将org.springframework.boot.autoconfigure.security 类别设置为 logINFO -level 消息。否则,将不打印默认密码。 |
---|
你可以通过提供spring.security.user.name
和spring.security.user.password
来更改用户名和密码。
默认情况下,在 Web 应用程序中获得的基本特性是:
a
UserDetailsService
(或ReactiveUserDetailsService
在 WebFlux 应用程序的情况下) Bean 具有内存存储和单个用户生成的密码(参见[Security属性.User
](https://DOCS. Spring.io/ Spring-boot/DOCS/2.6.4/api/org/springframework/boot/autoformit/autofigure/security/securityproperties.user.html)用于用户的属性)。基于表单的登录或 HTTP 基本安全性(取决于请求中的头)用于整个应用程序(如果致动器在 Classpath 上,则包括致动器端点)。
用于发布身份验证事件的
DefaultAuthenticationEventPublisher
。
你可以通过为它添加 Bean 来提供不同的AuthenticationEventPublisher
。
# 4.1.MVC 安全
默认的安全配置是在SecurityAutoConfiguration
和UserDetailsServiceAutoConfiguration
中实现的。SecurityAutoConfiguration
用于 Web 安全的 importsSpringBootWebSecurityConfiguration
和UserDetailsServiceAutoConfiguration
配置身份验证,这在非 Web 应用程序中也是相关的。要完全关闭默认的 Web 应用程序安全配置,或者合并多个 Spring 安全组件,例如 OAuth2 客户机和资源服务器,请添加类型SecurityFilterChain
的 Bean(这样做不会禁用UserDetailsService
配置或执行器的安全性)。
要同时关闭UserDetailsService
配置,可以添加类型为UserDetailsService
、AuthenticationProvider
或AuthenticationManager
的 Bean。
可以通过添加自定义SecurityFilterChain
或WebSecurityConfigurerAdapter
来覆盖访问规则 Bean。 Spring Boot 提供了方便的方法,这些方法可用于重写执行器端点和静态资源的访问规则。EndpointRequest
可用于创建RequestMatcher
这是基于management.endpoints.web.base-path
属性的。PathRequest
可用于为常用位置中的资源创建RequestMatcher
。
# 4.2.WebFlux 安全性
与 Spring MVC 应用程序类似,你可以通过添加spring-boot-starter-security
依赖项来保护你的 WebFlux 应用程序。默认的安全配置是在ReactiveSecurityAutoConfiguration
和UserDetailsServiceAutoConfiguration
中实现的。ReactiveSecurityAutoConfiguration
导入WebFluxSecurityConfiguration
用于 Web 安全和UserDetailsServiceAutoConfiguration
配置身份验证,这在非 Web 应用程序中也是相关的。要完全关闭默认的 Web 应用程序安全配置,可以添加类型WebFilterChainProxy
的 Bean(这样做不会禁用UserDetailsService
配置或执行器的安全性)。
要同时关闭UserDetailsService
配置,可以添加类型ReactiveUserDetailsService
或ReactiveAuthenticationManager
的 Bean。
Spring 使用诸如 OAuth2 客户端和资源服务器的多个安全组件的访问规则可以通过添加自定义 Bean 来配置。 Spring Boot 提供了方便的方法,这些方法可以用来覆盖执行器端点和静态资源的访问规则。EndpointRequest
可以用来创建一个ServerWebExchangeMatcher
,即基于management.endpoints.web.base-path
的属性。
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 (opens new window)是 Spring 支持的一个广泛使用的授权框架。
# 4.3.1.客户
如果你的 Classpath 上有spring-security-oauth2-client
,则可以利用一些自动配置来设置 OAuth2/Open ID Connect 客户端。此配置使用OAuth2Client属性
下的属性。相同的性质适用于 Servlet 和反应性应用。
你可以在spring.security.oauth2.client
前缀下注册多个 OAuth2 客户机和提供者,如以下示例所示:
属性
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 连接发现 (opens new window)的 OpenID Connect 提供者,可以进一步简化配置。提供程序需要配置issuer-uri
,这是它所断言的作为其发行者标识符的 URI。例如,如果提供的issuer-uri
是“https://example.com”,那么将对“https://example.com/.well-nown/openid-configuration”进行OpenID Provider Configuration Request
。预期结果是OpenID Provider Configuration Response
。下面的示例展示了如何使用issuer-uri
配置 OpenID Connect 提供程序:
属性
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
以使用不同的模式,则需要提供处理该自定义模式的配置。例如,对于 Servlet 应用程序,你可以添加你自己的类似于以下内容的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 引导自动配置由 Spring Security 用于管理客户端注册的InMemoryOAuth2AuthorizedClientService 的的 InMemoryOAuth2AuthorizedClientService 的功能有限,我们建议仅将其用于开发环境。对于生产环境,可以考虑使用 JdbcOAuth2AuthorizedClientService 或创建自己的OAuth2AuthorizedClientService 实现。 |
---|
# OAuth2 共同提供者的客户端注册
对于常见的 OAuth2 和 OpenID 提供者,包括 Google、GitHub、Facebook 和 OKTA,我们提供了一组提供者默认值(分别为google
、github
、facebook
和okta
)。
如果不需要自定义这些提供程序,可以将provider
属性设置为需要推断默认值的属性。此外,如果客户端注册的键与缺省支持的提供者匹配, Spring 启动也会推断出这一点。
换句话说,以下示例中的两个配置使用了 Google Provider:
属性
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.资源服务器
如果你的 Classpath 上有spring-security-oauth2-resource-server
,则 Spring 引导可以设置 OAuth2 资源服务器。对于 JWT 配置,需要指定一个 JWK 集 URI 或 OIDC Issuer URI,如以下示例所示:
属性
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"
属性
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 设置的 URI,则可以使用用于验证 JWT 签名的公钥配置资源服务器。 这可以使用 spring.security.oauth2.resourceserver.jwt.public-key-location 属性完成,值需要指向包含 PEM 编码的 X509 格式的公钥的文件。 |
---|
同样的性质也适用于 Servlet 和活性应用。
或者,你可以为 Servlet 应用程序定义自己的JwtDecoder
Bean,或者为反应性应用程序定义ReactiveJwtDecoder
。
在使用不透明令牌而不是 JWTS 的情况下,你可以配置以下属性来通过内省来验证令牌:
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"
同样,同样的性质也适用于 Servlet 和无反应的应用。
或者,你可以为 Servlet 应用程序定义自己的OpaqueTokenIntrospector
Bean,或者为反应性应用程序定义ReactiveOpaqueTokenIntrospector
。
# 4.3.3.授权服务器
Spring 目前,安全性不提供对实现 OAuth2.0 授权服务器的支持。然而,该功能可从Spring Security OAuth (opens new window)项目中获得,该项目最终将完全被 Spring 安全性所取代。在此之前,你可以使用spring-security-oauth2-autoconfigure
模块轻松地设置 OAuth2.0 授权服务器;有关说明,请参见其文件 (opens new window)。
# 4.4.SAML2.0
# 4.4.1.依赖方
如果你的 Classpath 上有spring-security-saml2-service-provider
,则可以利用一些自动配置来设置 SAML2.0 依赖方。此配置使用Saml2RelyingPartyProperties
下的属性。
依赖方注册表示身份提供程序 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 届会议
Spring 引导为广泛的数据存储范围提供Spring Session (opens new window)自动配置。 Servlet 在构建 Web 应用程序时,可以自动配置以下存储:
JDBC
雷迪斯
黑泽尔卡斯特
MongoDB
Servlet 自动配置取代了使用@Enable*HttpSession
的需要。
在构建反应式 Web 应用程序时,可以自动配置以下存储:
雷迪斯
MongoDB
反应式自动配置取代了使用@Enable*WebSession
的需要。
如果在 Classpath 上存在单个 Spring 会话模块,则 Spring 引导自动使用该存储实现。如果有多个实现,则必须选择希望用于存储会话的[StoreType
](https://github.com/ Spring-projects/ Spring-boot/tree/v2.6.4/ Spring-boot-project/ Spring-boot-autofigure/SRC/main/java/org/org/springframework/boot/autofigure/autofigure/session/storetype.java)。例如,要使用 JDBC 作为后端存储,你可以按以下方式配置应用程序:
Properties
spring.session.store-type=jdbc
Yaml
spring:
session:
store-type: "jdbc"
通过将store-type 设置为none ,可以禁用 Spring 会话。 |
---|
每个商店都有特定的附加设置。例如,可以自定义 JDBC 存储表的名称,如下例所示:
Properties
spring.session.jdbc.table-name=SESSIONS
Yaml
spring:
session:
jdbc:
table-name: "SESSIONS"
为了设置会话的超时,你可以使用spring.session.timeout
属性。如果在 Servlet Web 应用程序中未设置该属性,则自动配置将返回到server.servlet.session.timeout
的值。
你可以使用@Enable*HttpSession
( Servlet)或@Enable*WebSession
(反应式)来控制 Spring 会话的配置。这将导致自动配置后退。 Spring 然后可以使用注释的属性而不是先前描述的配置属性来配置会话。
# 6. Spring 仇恨
如果你开发了一个使用超媒体的 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 WebFlux 中使用 Spring Hateoas,可以在 org.springframework.hateoas:spring-hateoas 以及spring-boot-starter-webflux 上添加一个直接依赖项。 |
---|
# 7. 接下来要读什么?
你现在应该对如何使用 Spring 引导来开发 Web 应用程序有了很好的了解。接下来的几个部分描述了 Spring 引导如何集成各种数据技术、消息传递系统和其他 IO 功能。你可以根据应用程序的需求选择其中的任何一个。