Web
1. Servlet Web 应用程序
如果您想构建基于 servlet 的 Web 应用程序,您可以利用 Spring Boot 对 Spring MVC 或 Jersey 的自动配置。
1.1. “Spring Web MVC 框架”
Spring Web MVC 框架(通常称为“Spring MVC”)是一个丰富的“模型视图控制器”Web 框架。
Spring MVC 允许您创建特殊的@Controller
或@RestController
bean 来处理传入的 HTTP 请求。
控制器中的方法通过使用@RequestMapping
附注。
以下代码显示了典型的@RestController
提供 JSON 数据:
@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("/{userId}")
public User getUser(@PathVariable Long userId) {
return this.userRepository.findById(userId).get();
}
@GetMapping("/{userId}/customers")
public List<Customer> getUserCustomers(@PathVariable Long userId) {
return this.userRepository.findById(userId).map(this.customerRepository::findByUser).get();
}
@DeleteMapping("/{userId}")
public void deleteUser(@PathVariable Long userId) {
this.userRepository.deleteById(userId);
}
}
@RestController
@RequestMapping("/users")
class MyRestController(private val userRepository: UserRepository, private val customerRepository: CustomerRepository) {
@GetMapping("/{userId}")
fun getUser(@PathVariable userId: Long): User {
return userRepository.findById(userId).get()
}
@GetMapping("/{userId}/customers")
fun getUserCustomers(@PathVariable userId: Long): List<Customer> {
return userRepository.findById(userId).map(customerRepository::findByUser).get()
}
@DeleteMapping("/{userId}")
fun deleteUser(@PathVariable userId: Long) {
userRepository.deleteById(userId)
}
}
“WebMvc.fn”是功能变体,将路由配置与请求的实际处理分开,如以下示例所示:
@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();
}
}
@Configuration(proxyBeanMethods = false)
class MyRoutingConfiguration {
@Bean
fun routerFunction(userHandler: MyUserHandler): RouterFunction<ServerResponse> {
return RouterFunctions.route()
.GET("/{user}", ACCEPT_JSON, userHandler::getUser)
.GET("/{user}/customers", ACCEPT_JSON, userHandler::getUserCustomers)
.DELETE("/{user}", ACCEPT_JSON, userHandler::deleteUser)
.build()
}
companion object {
private val ACCEPT_JSON = accept(MediaType.APPLICATION_JSON)
}
}
@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();
}
}
@Component
class MyUserHandler {
fun getUser(request: ServerRequest?): ServerResponse {
return ServerResponse.ok().build()
}
fun getUserCustomers(request: ServerRequest?): ServerResponse {
return ServerResponse.ok().build()
}
fun deleteUser(request: ServerRequest?): ServerResponse {
return ServerResponse.ok().build()
}
}
Spring MVC 是核心 Spring Framework 的一部分,详细信息可在参考文档中找到。 spring.io/guides 上还有几个涵盖 Spring MVC 的指南。
您可以定义任意数量的RouterFunction beans 来模块化路由器的定义。
如果需要应用优先级,可以订购 Bean。 |
1.1.1. Spring MVC 自动配置
Spring Boot 为 Spring MVC 提供了自动配置,适用于大多数应用程序。
它取代了对@EnableWebMvc
并且两者不能一起使用。
除了 Spring MVC 的默认值外,自动配置还提供以下功能:
如果您想保留这些 Spring Boot MVC 自定义并进行更多 MVC 自定义(拦截器、格式化程序、视图控制器和其他功能),您可以添加自己的@Configuration
类型类WebMvcConfigurer
但没有 @EnableWebMvc
.
如果要提供RequestMappingHandlerMapping
,RequestMappingHandlerAdapter
或ExceptionHandlerExceptionResolver
,并且仍然保留 Spring Boot MVC 自定义,您可以声明类型为WebMvcRegistrations
并使用它来提供这些组件的自定义实例。
自定义实例将由 Spring MVC 进一步初始化和配置。
要参与并根据需要覆盖该后续处理,请WebMvcConfigurer
应该使用。
如果您不想使用自动配置并希望完全控制 Spring MVC,请添加您自己的@Configuration
注释为@EnableWebMvc
.
或者,添加您自己的@Configuration
-注释DelegatingWebMvcConfiguration
如 Javadoc 中所述@EnableWebMvc
.
1.1.2. Spring MVC 转换服务
Spring MVC 使用不同的ConversionService
转换为用于将值从application.properties
或application.yaml
文件。
这意味着Period
,Duration
和DataSize
转换器不可用,并且@DurationUnit
和@DataSizeUnit
注释将被忽略。
如果要自定义ConversionService
Spring MVC 使用时,你可以提供一个WebMvcConfigurer
带有addFormatters
方法。
通过此方法,您可以注册任何您喜欢的转换器,也可以委托给ApplicationConversionService
.
也可以使用spring.mvc.format.*
配置属性。
如果未配置,则使用以下默认值:
属性 | DateTimeFormatter |
---|---|
|
|
|
|
|
|
1.1.3. HttpMessage转换器
Spring MVC 使用HttpMessageConverter
接口来转换 HTTP 请求和响应。
合理的默认值是开箱即用的。
例如,对象可以自动转换为 JSON(使用 Jackson 库)或 XML(如果可用,则使用 Jackson XML 扩展,或者使用 JAXB(如果 Jackson XML 扩展不可用)。
默认情况下,字符串编码为UTF-8
.
如果您需要添加或自定义转换器,可以使用 Spring Boot 的HttpMessageConverters
类,如以下列表所示:
@Configuration(proxyBeanMethods = false)
public class MyHttpMessageConvertersConfiguration {
@Bean
public HttpMessageConverters customConverters() {
HttpMessageConverter<?> additional = new AdditionalHttpMessageConverter();
HttpMessageConverter<?> another = new AnotherHttpMessageConverter();
return new HttpMessageConverters(additional, another);
}
}
@Configuration(proxyBeanMethods = false)
class MyHttpMessageConvertersConfiguration {
@Bean
fun customConverters(): HttpMessageConverters {
val additional: HttpMessageConverter<*> = AdditionalHttpMessageConverter()
val another: HttpMessageConverter<*> = AnotherHttpMessageConverter()
return HttpMessageConverters(additional, another)
}
}
任何HttpMessageConverter
上下文中存在的 bean 将添加到转换器列表中。
您也可以以相同的方式覆盖默认转换器。
1.1.4. MessageCodes解析器
Spring MVC 有一个策略用于生成错误代码,用于从绑定错误中呈现错误消息:MessageCodesResolver
.
如果您将spring.mvc.message-codes-resolver-format
属性PREFIX_ERROR_CODE
或POSTFIX_ERROR_CODE
,Spring Boot 会为您创建一个(请参阅DefaultMessageCodesResolver.Format
).
1.1.5. 静态内容
默认情况下,Spring Boot 从名为/static
(或/public
或/resources
或/META-INF/resources
) 或从ServletContext
.
它使用ResourceHttpRequestHandler
Spring MVC 中的 Spring MVC,以便您可以通过添加自己的行为来修改该行为WebMvcConfigurer
并覆盖addResourceHandlers
方法。
在独立 Web 应用程序中,未启用容器中的缺省 servlet。
可以使用server.servlet.register-default-servlet
财产。
默认 servlet 充当后备,从ServletContext
如果 Spring 决定不处理它。
大多数时候,这不会发生(除非您修改默认的 MVC 配置),因为 Spring 始终可以通过DispatcherServlet
.
默认情况下,资源映射在 上,但您可以使用/**
spring.mvc.static-path-pattern
财产。
例如,将所有资源重新定位到/resources/**
可以按如下方式实现:
spring.mvc.static-path-pattern=/resources/**
spring:
mvc:
static-path-pattern: "/resources/**"
您还可以使用spring.web.resources.static-locations
属性(将默认值替换为目录位置列表)。
根 servlet 上下文路径 也会自动添加为位置。"/"
除了前面提到的“标准”静态资源位置之外,Webjars 内容还出现了一个特殊情况。
路径在/webjars/**
如果 jar 文件以 Webjars 格式打包,则从 jar 文件提供。
不要使用src/main/webapp 目录,如果您的应用程序打包为 jar。
虽然这个目录是一个通用标准,但它只适用于战争打包,如果你生成一个 jar,它会被大多数构建工具静默地忽略。 |
Spring Boot 还支持 Spring MVC 提供的高级资源处理功能,允许缓存破坏静态资源或对 Webjar 使用与版本无关的 URL 等用例。
要对 Webjar 使用与版本无关的 URL,请将webjars-locator-core
Dependency。
然后声明您的 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 . |
要使用缓存破坏,以下配置为所有静态资源配置缓存破坏解决方案,有效地添加内容哈希,例如<link href="/css/spring-2a2d595e6ed9a0b24f027f2b63b134d6.css"/>
,在 URL 中:
spring.web.resources.chain.strategy.content.enabled=true
spring.web.resources.chain.strategy.content.paths=/**
spring:
web:
resources:
chain:
strategy:
content:
enabled: true
paths: "/**"
资源链接在运行时在模板中重写,这要归功于ResourceUrlEncodingFilter 这是为 Thymeleaf 和 FreeMarker 自动配置的。
使用 JSP 时,您应该手动声明此过滤器。
其他模板引擎目前不自动支持,但可以与自定义模板宏/帮助程序一起使用,并使用ResourceUrlProvider . |
例如,使用 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
spring:
web:
resources:
chain:
strategy:
content:
enabled: true
paths: "/**"
fixed:
enabled: true
paths: "/js/lib/"
version: "v12"
通过此配置,位于"/js/lib/"
使用固定版本控制策略 ("/v12/js/lib/mymodule.js"
),而其他资源仍然使用内容 (<link href="/css/spring-2a2d595e6ed9a0b24f027f2b63b134d6.css"/>
).
看WebProperties.Resources
以获取更多支持的选项。
1.1.6. 欢迎页面
Spring Boot 支持静态和模板化欢迎页面。
它首先查找index.html
文件。
如果未找到,则查找index
模板。
如果找到其中任何一个,则会自动将其用作应用程序的欢迎页面。
1.1.8. 路径匹配和内容协商
Spring MVC 可以通过查看请求路径并将其与应用程序中定义的映射(例如,@GetMapping
Controller 方法上的注释)。
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")
:
spring.mvc.contentnegotiation.favor-parameter=true
spring:
mvc:
contentnegotiation:
favor-parameter: true
或者,如果您更喜欢使用其他参数名称:
spring.mvc.contentnegotiation.favor-parameter=true
spring.mvc.contentnegotiation.parameter-name=myparam
spring:
mvc:
contentnegotiation:
favor-parameter: true
parameter-name: "myparam"
大多数标准媒体类型都受支持,但您也可以定义新的媒体类型:
spring.mvc.contentnegotiation.media-types.markdown=text/markdown
spring:
mvc:
contentnegotiation:
media-types:
markdown: "text/markdown"
后缀模式匹配已弃用,并将在将来的版本中删除。 如果您了解注意事项,并且仍希望应用程序使用后缀模式匹配,则需要以下配置:
spring.mvc.contentnegotiation.favor-path-extension=true
spring.mvc.pathmatch.use-suffix-pattern=true
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
spring:
mvc:
contentnegotiation:
favor-path-extension: true
pathmatch:
use-registered-suffix-pattern: true
从 Spring Framework 5.3 开始,Spring MVC 支持两种将请求路径匹配到控制器的策略。
默认情况下,Spring Boot 使用PathPatternParser
策略。PathPatternParser
是一个优化的实现,但与AntPathMatcher
策略。PathPatternParser
限制某些路径模式变体的使用。
它也与后缀模式匹配 (spring.mvc.pathmatch.use-suffix-pattern
,spring.mvc.pathmatch.use-registered-suffix-pattern
) 并配置DispatcherServlet
使用路径前缀 (spring.mvc.servlet.path
).
可以使用spring.mvc.pathmatch.matching-strategy
configuration 属性,如以下示例所示:
spring.mvc.pathmatch.matching-strategy=ant-path-matcher
spring:
mvc:
pathmatch:
matching-strategy: "ant-path-matcher"
默认情况下,如果找不到请求的处理程序,Spring MVC 将发送 404 Not Found 错误响应。
要拥有一个NoHandlerFoundException
throw,请将 configprop:spring.mvc.throw-exception-if-no-handler-found 设置为true
. 请注意,默认情况下,静态内容的提供被映射到,因此将为所有请求提供一个处理程序。对于/**
NoHandlerFoundException
要被抛出,还必须设置spring.mvc.static-path-pattern
设置为更具体的值,例如/resources/**
或将spring.web.resources.add-mappings
自false
以完全禁用静态内容的投放。
1.1.9. 可配置的WebBinding初始化器
Spring MVC 使用WebBindingInitializer
初始化WebDataBinder
对于特定请求。如果您创建自己的ConfigurableWebBindingInitializer
@Bean
,Spring Boot 会自动配置 Spring MVC 以使用它。
1.1.10. 模板引擎
除了 REST Web 服务,您还可以使用 Spring MVC 来提供动态 HTML 内容。 Spring MVC 支持多种模板技术,包括 Thymeleaf、FreeMarker 和 JSP。 此外,许多其他模板引擎也包含自己的 Spring MVC 集成。
Spring Boot 包括对以下模板引擎的自动配置支持:
如果可能,应避免使用 JSP。将它们与嵌入式 servlet 容器一起使用时,有几个已知的限制。 |
当您将这些模板引擎之一与默认配置一起使用时,您的模板会自动从src/main/resources/templates
.
根据您运行应用程序的方式,您的 IDE 可能会以不同的方式对类路径进行排序。在 IDE 中从其 main 方法运行应用程序会导致与使用 Maven 或 Gradle 或从其打包的 jar 运行应用程序时不同的顺序。这可能导致 Spring Boot 无法找到预期的模板。如果遇到此问题,可以在 IDE 中重新排序类路径,以将模块的类和资源放在第一位。 |
1.1.11. 错误处理
默认情况下,Spring Boot 提供了一个/error
映射,以合理的方式处理所有错误,并在 servlet 容器中注册为“全局”错误页面。对于机器客户端,它会生成一个 JSON 响应,其中包含错误、HTTP 状态和异常消息的详细信息。对于浏览器客户端,有一个“白标”错误视图,它以 HTML 格式呈现相同的数据(要自定义它,请添加一个View
解析为error
).
有许多server.error
如果要自定义默认错误处理行为,可以设置属性。请参阅附录的“服务器属性”部分。
要完全替换默认行为,您可以实现ErrorController
并注册该类型的 Bean 定义或添加类型为ErrorAttributes
使用现有机制,但替换内容。
这BasicErrorController 可用作自定义ErrorController .
如果要为新内容类型添加处理程序,这特别有用(默认值是text/html 特别是为其他所有内容提供后备)。
为此,请扩展BasicErrorController ,添加一个带有@RequestMapping 有一个produces 属性,然后创建一个新类型的 bean。 |
您还可以定义一个用@ControllerAdvice
自定义要为特定控制器和/或异常类型返回的 JSON 文档,如以下示例所示:
@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;
}
}
@ControllerAdvice(basePackageClasses = [SomeController::class])
class MyControllerAdvice : ResponseEntityExceptionHandler() {
@ResponseBody
@ExceptionHandler(MyException::class)
fun handleControllerException(request: HttpServletRequest, ex: Throwable): ResponseEntity<*> {
val status = getStatus(request)
return ResponseEntity(MyErrorBody(status.value(), ex.message), status)
}
private fun getStatus(request: HttpServletRequest): HttpStatus {
val code = request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE) as Int
val status = HttpStatus.resolve(code)
return status ?: HttpStatus.INTERNAL_SERVER_ERROR
}
}
在前面的示例中,如果MyException
由与SomeController
,的 JSON 表示形式MyErrorBody
POJO 而不是ErrorAttributes
表示法。
在某些情况下,指标基础架构不会记录在控制器级别处理的错误。 应用程序可以通过将处理的异常设置为请求属性来确保此类异常与请求指标一起记录:
@Controller
public class MyController {
@ExceptionHandler(CustomException.class)
String handleCustomException(HttpServletRequest request, CustomException ex) {
request.setAttribute(ErrorAttributes.ERROR_ATTRIBUTE, ex);
return "errorView";
}
}
@Controller
class MyController {
@ExceptionHandler(CustomException::class)
fun handleCustomException(request: HttpServletRequest, ex: CustomException?): String {
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>
要映射所有5xx
错误,则目录结构如下:
src/ +- main/ +- java/ | + <source code> +- resources/ +- templates/ +- error/ | +- 5xx.ftlh +- <other templates>
对于更复杂的映射,您还可以添加实现ErrorViewResolver
接口,如以下示例所示:
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;
}
}
class MyErrorViewResolver : ErrorViewResolver {
override fun resolveErrorView(request: HttpServletRequest, status: HttpStatus,
model: Map<String, Any>): ModelAndView? {
// Use the request or status to optionally return a ModelAndView
if (status == HttpStatus.INSUFFICIENT_STORAGE) {
// We could add custom model values here
return ModelAndView("myview")
}
return null
}
}
您还可以使用常规的 Spring MVC 功能,例如@ExceptionHandler
方法和@ControllerAdvice
.
这ErrorController
然后选取任何未处理的异常。
在 Spring MVC 之外映射错误页面
对于不使用 Spring MVC 的应用程序,您可以使用ErrorPageRegistrar
直接注册的接口ErrorPages
.
此抽象直接与底层嵌入式 servlet 容器一起使用,即使您没有 Spring MVC 也可以工作DispatcherServlet
.
@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"));
}
}
@Configuration(proxyBeanMethods = false)
class MyErrorPagesConfiguration {
@Bean
fun errorPageRegistrar(): ErrorPageRegistrar {
return ErrorPageRegistrar { registry: ErrorPageRegistry -> registerErrorPages(registry) }
}
private fun registerErrorPages(registry: ErrorPageRegistry) {
registry.addErrorPages(ErrorPage(HttpStatus.BAD_REQUEST, "/400"))
}
}
如果您注册了ErrorPage 路径最终由Filter (就像一些非 Spring Web 框架(如 Jersey 和 Wicket)一样,那么Filter 必须显式注册为ERROR dispatcher,如以下示例所示: |
@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;
}
}
@Configuration(proxyBeanMethods = false)
class MyFilterConfiguration {
@Bean
fun myFilter(): FilterRegistrationBean<MyFilter> {
val registration = FilterRegistrationBean(MyFilter())
// ...
registration.setDispatcherTypes(EnumSet.allOf(DispatcherType::class.java))
return registration
}
}
请注意,默认的FilterRegistrationBean
不包括ERROR
调度程序类型。
WAR 部署中的错误处理
当部署到 servlet 容器时,Spring Boot 使用其错误页面过滤器将具有错误状态的请求转发到相应的错误页面。 这是必要的,因为 servlet 规范不提供用于注册错误页面的 API。 根据要将 war 文件部署到的容器和应用程序使用的技术,可能需要一些额外的配置。
如果响应尚未提交,错误页过滤器只能将请求转发到正确的错误页。
缺省情况下,WebSphere Application Server 8.0 及更高版本在成功完成 servlet 的服务方法后提交响应。
您应该通过设置com.ibm.ws.webcontainer.invokeFlushAfterService
自false
.
如果您使用的是 Spring Security 并希望在错误页面中访问主体,则必须将 Spring Security 的过滤器配置为在错误分派时调用。
为此,请将spring.security.filter.dispatcher-types
属性设置为async, error, forward, request
.
1.1.12. CORS 支持
从版本 4.2 开始,Spring MVC 支持 CORS。
使用控制器方法 CORS 配置@CrossOrigin
Spring Boot 应用程序中的注释不需要任何特定配置。全局 CORS 配置可以通过注册WebMvcConfigurer
带有定制的 beanaddCorsMappings(CorsRegistry)
方法,如以下示例所示:
@Configuration(proxyBeanMethods = false)
public class MyCorsConfiguration {
@Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/api/**");
}
};
}
}
@Configuration(proxyBeanMethods = false)
class MyCorsConfiguration {
@Bean
fun corsConfigurer(): WebMvcConfigurer {
return object : WebMvcConfigurer {
override fun addCorsMappings(registry: CorsRegistry) {
registry.addMapping("/api/**")
}
}
}
}
1.2. JAX-RS 和Jersey
如果您更喜欢 JAX-RS 编程模型作为 REST 端点,则可以使用可用的实现之一而不是 Spring MVC。Jersey 和 Apache CXF 开箱即用。
CXF 要求您注册其Servlet
或Filter
作为@Bean
在您的应用程序上下文中。
Jersey 有一些原生 Spring 支持,因此我们还在 Spring Boot 中为它提供了自动配置支持,以及一个Starters。
要开始使用 Jersey,请包含spring-boot-starter-jersey
作为依赖项,然后你需要一个@Bean
类型ResourceConfig
在其中注册所有终结点,如以下示例所示:
@Component
public class MyJerseyConfig extends ResourceConfig {
public MyJerseyConfig() {
register(MyEndpoint.class);
}
}
@Component
class MyJerseyConfig : ResourceConfig() {
init {
register(MyEndpoint::class.java)
}
}
Jersey对扫描可执行档案的支持相当有限。
例如,它无法扫描在完全可执行的 jar 文件中找到的包中的端点,或者WEB-INF/classes 运行可执行的 WAR 文件时。
为了避免这种限制,该packages 不应使用方法,并且应使用register 方法,如前面的示例所示。 |
对于更高级的自定义,您还可以注册任意数量的 bean,以实现ResourceConfigCustomizer
.
所有注册的端点都应是@Components
使用 HTTP 资源注释 (@GET
等),如以下示例所示:
@Component
@Path("/hello")
public class MyEndpoint {
@GET
public String message() {
return "Hello";
}
}
@Component
@Path("/hello")
class MyEndpoint {
@GET
fun message(): String {
return "Hello"
}
}
由于Endpoint
是Spring@Component
,其生命周期由 Spring 管理,您可以使用@Autowired
注释来注入依赖项,并使用@Value
注释以注入外部配置。
缺省情况下,Jersey servlet 已注册并映射到 。
您可以通过添加/*
@ApplicationPath
给你的ResourceConfig
.
默认情况下,Jersey 被设置为@Bean
类型ServletRegistrationBean
叫jerseyServletRegistration
.
默认情况下,servlet 是延迟初始化的,但您可以通过将spring.jersey.servlet.load-on-startup
.
您可以通过创建自己的一个具有相同名称的 Bean 来禁用或覆盖该 Bean。
您还可以通过将spring.jersey.type=filter
(在这种情况下,@Bean
替换或覆盖是jerseyFilterRegistration
).
过滤器有一个@Order
,您可以将其设置为spring.jersey.filter.order
.
使用 Jersey 作为过滤器时,必须存在一个 servlet,该 servlet 将处理 Jersey 未拦截的任何请求。
如果您的应用程序不包含这样的 servlet,您可能希望通过设置server.servlet.register-default-servlet
自true
.
servlet 和过滤器注册都可以通过使用spring.jersey.init.*
以指定属性映射。
1.3. 嵌入式 Servlet 容器支持
对于 servlet 应用程序,Spring Boot 包括对嵌入式 Tomcat、Jetty 和 Undertow 服务器的支持。
大多数开发人员使用适当的“Starter”来获取完全配置的实例。
默认情况下,嵌入式服务器在端口上侦听 HTTP 请求8080
.
1.3.1. Servlet、过滤器和侦听器
使用嵌入式 servlet 容器时,您可以注册 servlet、过滤器和所有侦听器(例如HttpSessionListener
)来自 Servlet 规范,通过使用 Spring Bean 或扫描 servlet 组件。
将 Servlet、过滤器和侦听器注册为 Spring Bean
任何Servlet
,Filter
或 Servlet*Listener
实例,即 Spring bean 已向嵌入式容器注册。
如果您想引用application.properties
在配置期间。
默认情况下,如果上下文仅包含单个 Servlet,则将其映射到 。
对于多个 Servlet Bean,Bean 名称用作路径前缀。
筛选器映射到 。/
/*
如果基于约定的映射不够灵活,您可以使用ServletRegistrationBean
,FilterRegistrationBean
和ServletListenerRegistrationBean
完全控制的类。
过滤豆不订购通常是安全的。
如果需要特定顺序,则应将Filter
跟@Order
或者让它实现Ordered
.
您无法配置Filter
通过使用@Order
.
如果您无法更改Filter
要添加的类@Order
或实现Ordered
,则必须定义一个FilterRegistrationBean
对于Filter
并使用setOrder(int)
方法。
避免配置读取请求正文的筛选器Ordered.HIGHEST_PRECEDENCE
,因为它可能与应用程序的字符编码配置相悖。
如果 servlet 过滤器包装请求,则应将其配置为小于或等于OrderedFilter.REQUEST_WRAPPER_FILTER_MAX_ORDER
.
要查看每个Filter 在您的应用程序中,为web 日志记录组 (logging.level.web=debug ).
然后,将在启动时记录已注册过滤器的详细信息,包括其顺序和 URL 模式。 |
注册时请注意Filter bean,因为它们在应用程序生命周期的早期就初始化了。
如果您需要注册Filter 与其他 bean 交互的 bean,请考虑使用DelegatingFilterProxyRegistrationBean 相反。 |
1.3.2. Servlet上下文初始化
嵌入式 servlet 容器不直接执行 servlet 3.0+javax.servlet.ServletContainerInitializer
接口或 Spring 的org.springframework.web.WebApplicationInitializer
接口。
这是一个有意的设计决策,旨在降低设计用于在战争中运行的第三方库可能破坏 Spring Boot 应用程序的风险。
如果您需要在 Spring Boot 应用程序中执行 servlet 上下文初始化,则应注册一个实现org.springframework.boot.web.servlet.ServletContextInitializer
接口。
单onStartup
方法提供对ServletContext
如有必要,可以轻松用作现有WebApplicationInitializer
.
1.3.3. ServletWebServerApplicationContext
在引擎盖下,Spring Boot 使用了不同类型的ApplicationContext
用于嵌入式 servlet 容器支持。
这ServletWebServerApplicationContext
是一种特殊类型的WebApplicationContext
通过搜索单个ServletWebServerFactory
豆。
通常TomcatServletWebServerFactory
,JettyServletWebServerFactory
或UndertowServletWebServerFactory
已自动配置。
您通常不需要了解这些实现类。
大多数应用程序都是自动配置的,并且适当的ApplicationContext 和ServletWebServerFactory 是代表您创建的。 |
在嵌入式容器设置中,ServletContext
设置为在应用程序上下文初始化期间发生的服务器启动的一部分。
因为这个豆子在ApplicationContext
无法使用ServletContext
.
解决此问题的一种方法是注入ApplicationContext
作为 bean 的依赖项,并访问ServletContext
只有在需要的时候。
另一种方法是在服务器启动后使用回调。
这可以使用ApplicationListener
它监听ApplicationStartedEvent
如下:
public class MyDemoBean implements ApplicationListener<ApplicationStartedEvent> {
private ServletContext servletContext;
@Override
public void onApplicationEvent(ApplicationStartedEvent event) {
ApplicationContext applicationContext = event.getApplicationContext();
this.servletContext = ((WebApplicationContext) applicationContext).getServletContext();
}
}
1.3.4. 自定义嵌入式 Servlet 容器
可以使用 Spring 配置常见的 servlet 容器设置Environment
性能。
通常,你会在application.properties
或application.yaml
文件。
常见的服务器设置包括:
Spring Boot 尽可能多地公开常见设置,但这并不总是可行的。
对于这些情况,专用命名空间提供特定于服务器的自定义(请参阅server.tomcat
和server.undertow
).
例如,可以使用嵌入式 servlet 容器的特定功能来配置访问日志。
请参阅ServerProperties class 获取完整列表。 |
SameSite Cookie
这SameSite
Cookie 属性可供 Web 浏览器使用,以控制是否以及如何在跨站点请求中提交 Cookie。
该属性与现代 Web 浏览器特别相关,这些浏览器已开始更改缺少该属性时使用的默认值。
如果要更改SameSite
属性,您可以使用server.servlet.session.cookie.same-site
财产。
自动配置的 Tomcat、Jetty 和 Undertow 服务器支持此属性。
它还用于配置基于 Spring Session servletSessionRepository
豆。
例如,如果您希望会话 cookie 具有SameSite
属性None
,您可以将以下内容添加到您的application.properties
或application.yaml
文件:
server.servlet.session.cookie.same-site=none
server:
servlet:
session:
cookie:
same-site: "none"
如果要更改SameSite
属性添加到您的HttpServletResponse
,您可以使用CookieSameSiteSupplier
.
这CookieSameSiteSupplier
被传递一个Cookie
并可能返回一个SameSite
值,或null
.
您可以使用许多便利的工厂和过滤方法来快速匹配特定 Cookie。
例如,添加以下 bean 将自动应用SameSite
之Lax
对于名称与正则表达式匹配的所有 cookiemyapp.*
.
@Configuration(proxyBeanMethods = false)
public class MySameSiteConfiguration {
@Bean
public CookieSameSiteSupplier applicationCookieSameSiteSupplier() {
return CookieSameSiteSupplier.ofLax().whenHasNameMatching("myapp.*");
}
}
@Configuration(proxyBeanMethods = false)
class MySameSiteConfiguration {
@Bean
fun applicationCookieSameSiteSupplier(): CookieSameSiteSupplier {
return CookieSameSiteSupplier.ofLax().whenHasNameMatching("myapp.*")
}
}
字符编码
用于请求和响应处理的嵌入式 servlet 容器的字符编码行为可以使用server.servlet.encoding.*
配置属性。
当请求的Accept-Language
header 表示请求的语言环境,它将由 servlet 容器自动映射到字符集。
每个容器提供默认区域设置到字符集映射,您应该验证它们是否满足应用程序的需求。
如果不这样做,请使用server.servlet.encoding.mapping
configuration 属性来自定义映射,如以下示例所示:
server.servlet.encoding.mapping.ko=UTF-8
server:
servlet:
encoding:
mapping:
ko: "UTF-8"
在前面的示例中,ko
(韩语)区域设置已映射到UTF-8
.
这相当于<locale-encoding-mapping-list>
在web.xml
传统战争部署的文件。
程序化定制
如果您需要以编程方式配置嵌入式 servlet 容器,您可以注册一个实现WebServerFactoryCustomizer
接口。WebServerFactoryCustomizer
提供对ConfigurableServletWebServerFactory
,其中包括许多自定义 setter 方法。
以下示例演示以编程方式设置端口:
@Component
public class MyWebServerFactoryCustomizer implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> {
@Override
public void customize(ConfigurableServletWebServerFactory server) {
server.setPort(9000);
}
}
@Component
class MyWebServerFactoryCustomizer : WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> {
override fun customize(server: ConfigurableServletWebServerFactory) {
server.setPort(9000)
}
}
TomcatServletWebServerFactory
,JettyServletWebServerFactory
和UndertowServletWebServerFactory
是ConfigurableServletWebServerFactory
分别为 Tomcat、Jetty 和 Undertow 具有额外的自定义 setter 方法。
以下示例演示如何自定义TomcatServletWebServerFactory
提供对特定于 Tomcat 的配置选项的访问:
@Component
public class MyTomcatWebServerFactoryCustomizer implements WebServerFactoryCustomizer<TomcatServletWebServerFactory> {
@Override
public void customize(TomcatServletWebServerFactory server) {
server.addConnectorCustomizers((connector) -> connector.setAsyncTimeout(Duration.ofSeconds(20).toMillis()));
}
}
@Component
class MyTomcatWebServerFactoryCustomizer : WebServerFactoryCustomizer<TomcatServletWebServerFactory> {
override fun customize(server: TomcatServletWebServerFactory) {
server.addConnectorCustomizers({ connector -> connector.asyncTimeout = Duration.ofSeconds(20).toMillis() })
}
}
直接自定义 ConfigurableServletWebServerFactory
对于需要从ServletWebServerFactory
,您可以自己公开此类 bean。
为许多配置选项提供了 setter。 如果您需要做一些更奇特的事情,还提供了几个受保护的方法“钩子”。 有关详细信息,请参阅源代码文档。
自动配置的定制器仍应用于自定义工厂,因此请谨慎使用该选项。 |
2. 响应式 Web 应用程序
Spring Boot 通过为 Spring Webflux 提供自动配置,简化了响应式 Web 应用程序的开发。
2.1. “Spring WebFlux 框架”
Spring WebFlux 是 Spring Framework 5.0 中引入的新响应式 Web 框架。 与 Spring MVC 不同的是,它不需要 servlet API,是完全异步和非阻塞的,并通过 Reactor 项目实现 Reactive Streams 规范。
Spring WebFlux 有两种类型:函数式和基于注释的。 基于注释的非常接近 Spring MVC 模型,如以下示例所示:
@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("/{userId}")
public Mono<User> getUser(@PathVariable Long userId) {
return this.userRepository.findById(userId);
}
@GetMapping("/{userId}/customers")
public Flux<Customer> getUserCustomers(@PathVariable Long userId) {
return this.userRepository.findById(userId).flatMapMany(this.customerRepository::findByUser);
}
@DeleteMapping("/{userId}")
public Mono<Void> deleteUser(@PathVariable Long userId) {
return this.userRepository.deleteById(userId);
}
}
@RestController
@RequestMapping("/users")
class MyRestController(private val userRepository: UserRepository, private val customerRepository: CustomerRepository) {
@GetMapping("/{userId}")
fun getUser(@PathVariable userId: Long): Mono<User?> {
return userRepository.findById(userId)
}
@GetMapping("/{userId}/customers")
fun getUserCustomers(@PathVariable userId: Long): Flux<Customer> {
return userRepository.findById(userId).flatMapMany { user: User? ->
customerRepository.findByUser(user)
}
}
@DeleteMapping("/{userId}")
fun deleteUser(@PathVariable userId: Long): Mono<Void> {
return userRepository.deleteById(userId)
}
}
“WebFlux.fn”是功能变体,将路由配置与请求的实际处理分开,如以下示例所示:
@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();
}
}
@Configuration(proxyBeanMethods = false)
class MyRoutingConfiguration {
@Bean
fun monoRouterFunction(userHandler: MyUserHandler): RouterFunction<ServerResponse> {
return RouterFunctions.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)
}
companion object {
private val ACCEPT_JSON = accept(MediaType.APPLICATION_JSON)
}
}
@Component
public class MyUserHandler {
public Mono<ServerResponse> getUser(ServerRequest request) {
...
}
public Mono<ServerResponse> getUserCustomers(ServerRequest request) {
...
}
public Mono<ServerResponse> deleteUser(ServerRequest request) {
...
}
}
@Component
class MyUserHandler {
fun getUser(request: ServerRequest?): Mono<ServerResponse> {
return ServerResponse.ok().build()
}
fun getUserCustomers(request: ServerRequest?): Mono<ServerResponse> {
return ServerResponse.ok().build()
}
fun deleteUser(request: ServerRequest?): Mono<ServerResponse> {
return ServerResponse.ok().build()
}
}
WebFlux 是 Spring Framework 的一部分,详细信息在其参考文档中可用。
您可以定义任意数量的RouterFunction beans 来模块化路由器的定义。
如果需要应用优先级,可以订购 Bean。 |
要开始使用,请添加spring-boot-starter-webflux
模块添加到您的应用程序中。
将两者都添加spring-boot-starter-web 和spring-boot-starter-webflux 模块导致 Spring Boot 自动配置 Spring MVC,而不是 WebFlux。
之所以选择这种行为,是因为许多 Spring 开发人员将spring-boot-starter-webflux 到他们的 Spring MVC 应用程序以使用响应式WebClient .
您仍然可以通过将所选应用程序类型设置为SpringApplication.setWebApplicationType(WebApplicationType.REACTIVE) . |
2.1.1. Spring WebFlux 自动配置
Spring Boot 为 Spring WebFlux 提供了自动配置,适用于大多数应用程序。
自动配置在 Spring 的默认值之上添加了以下功能:
如果您想保留 Spring Boot WebFlux 功能并想要添加额外的 WebFlux 配置,您可以添加自己的配置@Configuration
类型类WebFluxConfigurer
但没有 @EnableWebFlux
.
如果您想完全控制 Spring WebFlux,您可以添加自己的@Configuration
注释为@EnableWebFlux
.
2.1.2. Spring WebFlux 转换服务
如果要自定义ConversionService
Spring WebFlux 使用时,你可以提供一个WebFluxConfigurer
带有addFormatters
方法。
也可以使用spring.webflux.format.*
配置属性。
如果未配置,则使用以下默认值:
属性 | DateTimeFormatter |
---|---|
|
|
|
|
|
|
2.1.3. 带有 HttpMessageReaders 和 HttpMessageWriters 的 HTTP 编解码器
Spring WebFlux 使用HttpMessageReader
和HttpMessageWriter
接口来转换 HTTP 请求和响应。
它们配置为CodecConfigurer
通过查看类路径中可用的库来获得合理的默认值。
Spring Boot 为编解码器提供了专用的配置属性,spring.codec.*
.
它还通过使用CodecCustomizer
实例。
例如spring.jackson.*
配置密钥应用于 Jackson 编解码器。
如果需要添加或自定义编解码器,可以创建自定义CodecCustomizer
组件,如以下示例所示:
@Configuration(proxyBeanMethods = false)
public class MyCodecsConfiguration {
@Bean
public CodecCustomizer myCodecCustomizer() {
return (configurer) -> {
configurer.registerDefaults(false);
configurer.customCodecs().register(new ServerSentEventHttpMessageReader());
// ...
};
}
}
class MyCodecsConfiguration {
@Bean
fun myCodecCustomizer(): CodecCustomizer {
return CodecCustomizer { configurer: CodecConfigurer ->
configurer.registerDefaults(false)
configurer.customCodecs().register(ServerSentEventHttpMessageReader())
}
}
}
您还可以利用 Boot 的自定义 JSON 序列化器和反序列化程序。
2.1.4. 静态内容
默认情况下,Spring Boot 从名为/static
(或/public
或/resources
或/META-INF/resources
) 在类路径中。
它使用ResourceWebHandler
从 Spring WebFlux 中,这样你就可以通过添加自己的行为来修改该行为WebFluxConfigurer
并覆盖addResourceHandlers
方法。
默认情况下,资源映射在 上,但您可以通过将/**
spring.webflux.static-path-pattern
财产。
例如,将所有资源重新定位到/resources/**
可以按如下方式实现:
spring.webflux.static-path-pattern=/resources/**
spring:
webflux:
static-path-pattern: "/resources/**"
您还可以使用spring.web.resources.static-locations
.
这样做会将默认值替换为目录位置列表。
如果这样做,默认欢迎页面检测将切换到自定义位置。
因此,如果有一个index.html
在启动时的任何位置,它都是应用程序的主页。
除了前面列出的“标准”静态资源位置之外,Webjars 内容还出现了一个特殊情况。
路径在/webjars/**
如果 jar 文件以 Webjars 格式打包,则从 jar 文件提供。
Spring WebFlux 应用程序并不严格依赖于 servlet API,因此它们不能部署为 war 文件,并且不使用src/main/webapp 目录。 |
2.1.5. 欢迎页面
Spring Boot 支持静态和模板化欢迎页面。
它首先查找index.html
文件。
如果未找到,则查找index
模板。
如果找到其中任何一个,则会自动将其用作应用程序的欢迎页面。
2.1.6. 模板引擎
除了 REST Web 服务,您还可以使用 Spring WebFlux 来提供动态 HTML 内容。 Spring WebFlux 支持多种模板技术,包括 Thymeleaf、FreeMarker 和 Mustache。
Spring Boot 包括对以下模板引擎的自动配置支持:
当您将这些模板引擎之一与默认配置一起使用时,您的模板会自动从src/main/resources/templates
.
2.1.7. 错误处理
Spring Boot 提供了一个WebExceptionHandler
以合理的方式处理所有错误。
它在处理顺序中的位置紧接在 WebFlux 提供的处理程序之前,这些处理程序被认为是最后的。
对于机器客户端,它会生成一个 JSON 响应,其中包含错误、HTTP 状态和异常消息的详细信息。
对于浏览器客户端,有一个“白标”错误处理程序,它以 HTML 格式呈现相同的数据。
您还可以提供自己的 HTML 模板来显示错误(请参阅下一节)。
自定义此功能的第一步通常涉及使用现有机制,但替换或增强错误内容。
为此,您可以添加一个类型的 beanErrorAttributes
.
要更改错误处理行为,您可以实现ErrorWebExceptionHandler
并注册该类型的 bean 定义。
因为ErrorWebExceptionHandler
相当低级,Spring Boot 还提供了一个方便的AbstractErrorWebExceptionHandler
以允许您以 WebFlux 函数方式处理错误,如以下示例所示:
@Component
public class MyErrorWebExceptionHandler extends AbstractErrorWebExceptionHandler {
public MyErrorWebExceptionHandler(ErrorAttributes errorAttributes, WebProperties webProperties,
ApplicationContext applicationContext, ServerCodecConfigurer serverCodecConfigurer) {
super(errorAttributes, webProperties.getResources(), applicationContext);
setMessageReaders(serverCodecConfigurer.getReaders());
setMessageWriters(serverCodecConfigurer.getWriters());
}
@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();
}
}
@Component
class MyErrorWebExceptionHandler(
errorAttributes: ErrorAttributes, webProperties: WebProperties,
applicationContext: ApplicationContext, serverCodecConfigurer: ServerCodecConfigurer
) : AbstractErrorWebExceptionHandler(errorAttributes, webProperties.resources, applicationContext) {
init {
setMessageReaders(serverCodecConfigurer.readers)
setMessageWriters(serverCodecConfigurer.writers)
}
override fun getRoutingFunction(errorAttributes: ErrorAttributes): RouterFunction<ServerResponse> {
return RouterFunctions.route(this::acceptsXml, this::handleErrorAsXml)
}
private fun acceptsXml(request: ServerRequest): Boolean {
return request.headers().accept().contains(MediaType.APPLICATION_XML)
}
fun handleErrorAsXml(request: ServerRequest): Mono<ServerResponse> {
val builder = ServerResponse.status(HttpStatus.INTERNAL_SERVER_ERROR)
// ... additional builder calls
return builder.build()
}
}
为了获得更完整的图片,您还可以进行子类DefaultErrorWebExceptionHandler
直接并覆盖特定方法。
在某些情况下,指标基础架构不会记录在控制器或处理程序函数级别处理的错误。 应用程序可以通过将处理的异常设置为请求属性来确保此类异常与请求指标一起记录:
@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();
}
}
@Controller
class MyExceptionHandlingController {
@GetMapping("/profile")
fun userProfile(): Rendering {
// ...
throw IllegalStateException()
}
@ExceptionHandler(IllegalStateException::class)
fun handleIllegalState(exchange: ServerWebExchange, exc: IllegalStateException): Rendering {
exchange.attributes.putIfAbsent(ErrorAttributes.ERROR_ATTRIBUTE, exc)
return Rendering.view("errorView").modelAttribute("message", exc.message ?: "").build()
}
}
自定义错误页面
如果要显示给定状态代码的自定义 HTML 错误页,可以添加从error/*
,例如,通过将文件添加到/error
目录。
错误页面可以是静态 HTML(即添加到任何静态资源目录下)或使用模板构建。
文件名应为确切的状态代码、状态代码系列掩码或error
如果没有其他匹配项,则为默认值。
请注意,默认错误视图的路径是error/error
,而对于 Spring MVC,默认错误视图是error
.
例如,要映射404
到静态 HTML 文件,您的目录结构如下所示:
src/
+- main/
+- java/
| + <source code>
+- resources/
+- public/
+- error/
| +- 404.html
+- <other public assets>
要映射所有5xx
错误,则目录结构如下所示:
src/
+- main/
+- java/
| + <source code>
+- resources/
+- templates/
+- error/
| +- 5xx.mustache
+- <other templates>
2.1.8. Web 过滤器
Spring WebFlux 提供了一个WebFilter
接口,可以实现来过滤 HTTP 请求-响应交换。WebFilter
在应用程序上下文中找到的 bean 将自动用于过滤每个交换。
在过滤器的顺序很重要的地方,他们可以实施Ordered
或用@Order
.
Spring Boot 自动配置可能会为您配置 Web 过滤器。
当它这样做时,将使用下表中显示的订单:
网页过滤器 | 次序 |
---|---|
|
|
|
|
|
|
2.2. 嵌入式响应式服务器支持
Spring Boot 包括对以下嵌入式响应式 Web 服务器的支持:Reactor Netty、Tomcat、Jetty 和 Undertow。 大多数开发人员使用适当的“Starter”来获取完全配置的实例。 默认情况下,嵌入式服务器在端口 8080 上侦听 HTTP 请求。
2.2.1. 自定义响应式服务器
可以使用 Spring 配置常见的响应式 Web 服务器设置Environment
性能。
通常,你会在application.properties
或application.yaml
文件。
常见的服务器设置包括:
Spring Boot 尽可能多地公开常见设置,但这并不总是可行的。
对于这些情况,专用命名空间(例如server.netty.*
提供特定于服务器的自定义。
请参阅ServerProperties class 获取完整列表。 |
程序化定制
如果您需要以编程方式配置响应式 Web 服务器,您可以注册一个实现WebServerFactoryCustomizer
接口。WebServerFactoryCustomizer
提供对ConfigurableReactiveWebServerFactory
,其中包括许多自定义 setter 方法。
以下示例演示以编程方式设置端口:
@Component
public class MyWebServerFactoryCustomizer implements WebServerFactoryCustomizer<ConfigurableReactiveWebServerFactory> {
@Override
public void customize(ConfigurableReactiveWebServerFactory server) {
server.setPort(9000);
}
}
@Component
class MyWebServerFactoryCustomizer : WebServerFactoryCustomizer<ConfigurableReactiveWebServerFactory> {
override fun customize(server: ConfigurableReactiveWebServerFactory) {
server.setPort(9000)
}
}
JettyReactiveWebServerFactory
,NettyReactiveWebServerFactory
,TomcatReactiveWebServerFactory
和UndertowServletWebServerFactory
是ConfigurableReactiveWebServerFactory
分别为 Jetty、Reactor Netty、Tomcat 和 Undertow 提供额外的自定义 setter 方法。
以下示例演示如何自定义NettyReactiveWebServerFactory
提供对 Reactor Netty 特定配置选项的访问:
@Component
public class MyNettyWebServerFactoryCustomizer implements WebServerFactoryCustomizer<NettyReactiveWebServerFactory> {
@Override
public void customize(NettyReactiveWebServerFactory factory) {
factory.addServerCustomizers((server) -> server.idleTimeout(Duration.ofSeconds(20)));
}
}
@Component
class MyNettyWebServerFactoryCustomizer : WebServerFactoryCustomizer<NettyReactiveWebServerFactory> {
override fun customize(factory: NettyReactiveWebServerFactory) {
factory.addServerCustomizers({ server -> server.idleTimeout(Duration.ofSeconds(20)) })
}
}
直接自定义 ConfigurableReactiveWebServerFactory
对于需要从ReactiveWebServerFactory
,您可以自己公开此类 bean。
为许多配置选项提供了 setter。 如果您需要做一些更奇特的事情,还提供了几个受保护的方法“钩子”。 有关详细信息,请参阅源代码文档。
自动配置的定制器仍应用于自定义工厂,因此请谨慎使用该选项。 |
2.3. 响应式服务器资源配置
当自动配置 Reactor Netty 或 Jetty 服务器时,Spring Boot 将创建特定的 bean,这些 bean 将向服务器实例提供 HTTP 资源:ReactorResourceFactory
或JettyResourceFactory
.
默认情况下,这些资源还将与 Reactor Netty 和 Jetty 客户端共享,以获得最佳性能,给定:
-
服务器和客户端使用相同的技术
-
客户端实例是使用
WebClient.Builder
由 Spring Boot 自动配置的 bean
开发人员可以通过提供自定义ReactorResourceFactory
或JettyResourceFactory
bean - 这将应用于客户端和服务器。
您可以在 WebClient Runtime 部分了解有关客户端资源配置的更多信息。
3. 优雅关机
所有四个嵌入式 Web 服务器(Jetty、Reactor Netty、Tomcat 和 Undertow)以及响应式和基于 servlet 的 Web 应用程序都支持正常关闭。
它作为关闭应用程序上下文的一部分发生,并在停止的最早阶段执行SmartLifecycle
豆。
此停止处理使用超时,该超时提供宽限期,在此期间将允许完成现有请求,但不允许新请求。
不允许新请求的确切方式因所使用的 Web 服务器而异。
Jetty、Reactor Netty 和 Tomcat 将停止接受网络层的请求。
Undertow 将接受请求,但会立即回复服务不可用 (503) 响应。
使用 Tomcat 正常关闭需要 Tomcat 9.0.33 或更高版本。 |
要启用正常关机,请配置server.shutdown
属性,如以下示例所示:
server.shutdown=graceful
server:
shutdown: "graceful"
要配置超时期限,请配置spring.lifecycle.timeout-per-shutdown-phase
属性,如以下示例所示:
spring.lifecycle.timeout-per-shutdown-phase=20s
spring:
lifecycle:
timeout-per-shutdown-phase: "20s"
如果 IDE 未发送适当的 IDE,则在 IDE 中使用正常关机可能无法正常工作SIGTERM 信号。
有关更多详细信息,请参阅 IDE 的文档。 |
4. Spring Security
如果 Spring Security 位于类路径上,则默认情况下 Web 应用程序受到保护。
Spring Boot 依靠 Spring Security 的内容协商策略来确定是否使用httpBasic
或formLogin
.
要向 Web 应用程序添加方法级安全性,您还可以将@EnableGlobalMethodSecurity
使用您想要的设置。
可以在 Spring Security参考指南中找到其他信息。
默认值UserDetailsService
有一个用户。
用户名是user
,并且密码是随机的,并在应用程序启动时以 WARN 级别打印,如以下示例所示:
Using generated security password: 78fa095d-3f4c-48b1-ad50-e24c31d5cf35 This generated password is for development use only. Your security configuration must be updated before running your application in production.
如果微调日志记录配置,请确保org.springframework.boot.autoconfigure.security category 设置为 logWARN -level 消息。
否则,不会打印默认密码。 |
您可以通过提供spring.security.user.name
和spring.security.user.password
.
默认情况下,您在 Web 应用程序中获得的基本功能包括:
-
一个
UserDetailsService
(或ReactiveUserDetailsService
对于 WebFlux 应用程序)具有内存存储的 bean 和具有生成密码的单个用户(请参阅SecurityProperties.User
)的属性。 -
基于表单的登录或 HTTP Basic 安全性(取决于
Accept
标头)的标头)。 -
一个
DefaultAuthenticationEventPublisher
用于发布身份验证事件。
您可以提供不同的AuthenticationEventPublisher
通过为它添加一个豆子。
4.1. MVC 安全性
默认安全配置在SecurityAutoConfiguration
和UserDetailsServiceAutoConfiguration
.SecurityAutoConfiguration
进口SpringBootWebSecurityConfiguration
用于 Web 安全和UserDetailsServiceAutoConfiguration
配置身份验证,这在非 Web 应用程序中也相关。
要完全关闭默认的Web应用程序安全配置或组合多个Spring Security组件(例如OAuth2客户端和资源服务器),请添加类型为“Ber”的beanSecurityFilterChain
(这样做不会禁用UserDetailsService
配置或执行器的安全性)。
要同时关闭UserDetailsService
配置,您可以添加类型为UserDetailsService
,AuthenticationProvider
或AuthenticationManager
.
可以通过添加自定义SecurityFilterChain
或WebSecurityConfigurerAdapter
豆。
Spring Boot 提供了方便的方法,可用于覆盖执行器端点和静态资源的访问规则。EndpointRequest
可用于创建RequestMatcher
即基于management.endpoints.web.base-path
财产。PathRequest
可用于创建RequestMatcher
用于常用位置的资源。
4.2. WebFlux 安全性
与 Spring MVC 应用程序类似,您可以通过添加spring-boot-starter-security
Dependency。
默认安全配置在ReactiveSecurityAutoConfiguration
和UserDetailsServiceAutoConfiguration
.ReactiveSecurityAutoConfiguration
进口WebFluxSecurityConfiguration
用于 Web 安全和UserDetailsServiceAutoConfiguration
配置身份验证,这在非 Web 应用程序中也相关。
要完全关闭默认的 Web 应用程序安全配置,您可以添加一个 bean 类型WebFilterChainProxy
(这样做不会禁用UserDetailsService
配置或执行器的安全性)。
要同时关闭UserDetailsService
配置,您可以添加类型为ReactiveUserDetailsService
或ReactiveAuthenticationManager
.
可以通过添加自定义SecurityWebFilterChain
豆。
Spring Boot 提供了方便的方法,可用于覆盖执行器端点和静态资源的访问规则。EndpointRequest
可用于创建ServerWebExchangeMatcher
即基于management.endpoints.web.base-path
财产。
PathRequest
可用于创建ServerWebExchangeMatcher
用于常用位置的资源。
例如,您可以通过添加以下内容来自定义安全配置:
@Configuration(proxyBeanMethods = false)
public class MyWebFluxSecurityConfiguration {
@Bean
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
http.authorizeExchange((exchange) -> {
exchange.matchers(PathRequest.toStaticResources().atCommonLocations()).permitAll();
exchange.pathMatchers("/foo", "/bar").authenticated();
});
http.formLogin(withDefaults());
return http.build();
}
}
@Configuration(proxyBeanMethods = false)
class MyWebFluxSecurityConfiguration {
@Bean
fun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
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
. 相同的属性适用于servlet和响应式应用程序。
您可以在spring.security.oauth2.client
prefix,如以下示例所示:
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
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 发现的 OpenID Connect 提供程序,可以进一步简化配置。需要对提供程序进行配置issuer-uri
这是它断言为其颁发者标识符的 URI。例如,如果issuer-uri
provided 是“https://example.com”,则OpenID Provider Configuration Request
将被设置为“https://example.com/.well-known/openid-configuration”。结果预计为OpenID Provider Configuration Response
. 以下示例显示如何使用issuer-uri
:
spring.security.oauth2.client.provider.oidc-provider.issuer-uri=https://dev-123456.oktapreview.com/oauth2/default/
spring:
security:
oauth2:
client:
provider:
oidc-provider:
issuer-uri: "https://dev-123456.oktapreview.com/oauth2/default/"
默认情况下,Spring Security 的OAuth2LoginAuthenticationFilter
仅处理匹配的 URL/login/oauth2/code/*
. 如果要自定义redirect-uri
要使用不同的模式,您需要提供配置来处理该自定义模式。例如,对于 servlet 应用程序,您可以添加自己的SecurityFilterChain
类似于以下内容:
@Configuration(proxyBeanMethods = false)
public class MyOAuthClientConfiguration {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.authorizeRequests((requests) -> requests.anyRequest().authenticated());
http.oauth2Login((login) -> login.redirectionEndpoint().baseUri("custom-callback"));
return http.build();
}
}
@Configuration(proxyBeanMethods = false)
class MyOAuthClientConfiguration {
@Bean
fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
http.authorizeRequests().anyRequest().authenticated()
http.oauth2Login().redirectionEndpoint().baseUri("custom-callback")
return http.build()
}
}
Spring Boot 自动配置InMemoryOAuth2AuthorizedClientService Spring Security 使用它来管理客户端注册。 这InMemoryOAuth2AuthorizedClientService 功能有限,建议仅将其用于开发环境。对于生产环境,请考虑使用JdbcOAuth2AuthorizedClientService 或创建您自己的实现OAuth2AuthorizedClientService . |
通用提供商的 OAuth2 客户端注册
对于常见的 OAuth2 和 OpenID 提供商,包括 Google、Github、Facebook 和 Okta,我们提供了一组提供商默认值 (google
,github
,facebook
和okta
,分别)。
如果您不需要自定义这些提供程序,您可以将provider
属性到您需要推断默认值的那个。
此外,如果客户端注册的密钥与默认支持的提供程序匹配,Spring Boot 也会推断出这一点。
换句话说,以下示例中的两个配置使用 Google 提供程序:
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
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 Set URI 或 OIDC Issuer URI,如以下示例所示:
spring.security.oauth2.resourceserver.jwt.jwk-set-uri=https://example.com/oauth2/default/v1/keys
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/
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 格式的公钥的文件。 |
这spring.security.oauth2.resourceserver.jwt.audiences
属性可用于指定 JWT 中 aud 声明的预期值。
例如,要求 JWT 包含值my-audience
:
spring.security.oauth2.resourceserver.jwt.audiences[0]=my-audience
spring:
security:
oauth2:
resourceserver:
jwt:
audiences:
- "my-audience"
相同的属性适用于 servlet 和响应式应用程序。
或者,您可以定义自己的JwtDecoder
用于 servlet 应用程序的 bean 或ReactiveJwtDecoder
用于响应式应用。
如果使用不透明Tokens而不是 JWT,您可以配置以下属性以通过自省验证Tokens:
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
spring:
security:
oauth2:
resourceserver:
opaquetoken:
introspection-uri: "https://example.com/check-token"
client-id: "my-client-id"
client-secret: "my-client-secret"
同样,相同的属性适用于 servlet 和响应式应用程序。
或者,您可以定义自己的OpaqueTokenIntrospector
用于 servlet 应用程序的 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
.
信赖方注册表示标识提供者 IDP 和服务提供商 SP 之间的配对配置。
您可以在spring.security.saml2.relyingparty
prefix,如以下示例所示:
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.singlelogout.url=https://myapp/logout/saml2/slo
spring.security.saml2.relyingparty.registration.my-relying-party1.singlelogout.response-url=https://remoteidp2.slo.url
spring.security.saml2.relyingparty.registration.my-relying-party1.singlelogout.binding=POST
spring.security.saml2.relyingparty.registration.my-relying-party1.assertingparty.verification.credentials[0].certificate-location=path-to-verification-cert
spring.security.saml2.relyingparty.registration.my-relying-party1.assertingparty.entity-id=remote-idp-entity-id1
spring.security.saml2.relyingparty.registration.my-relying-party1.assertingparty.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.assertingparty.verification.credentials[0].certificate-location=path-to-other-verification-cert
spring.security.saml2.relyingparty.registration.my-relying-party2.assertingparty.entity-id=remote-idp-entity-id2
spring.security.saml2.relyingparty.registration.my-relying-party2.assertingparty.sso-url=https://remoteidp2.sso.url
spring.security.saml2.relyingparty.registration.my-relying-party2.assertingparty.singlelogout.url=https://remoteidp2.slo.url
spring.security.saml2.relyingparty.registration.my-relying-party2.assertingparty.singlelogout.reponse-url=https://myapp/logout/saml2/slo
spring.security.saml2.relyingparty.registration.my-relying-party2.assertingparty.singlelogout.binding=POST
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"
singlelogout:
url: "https://myapp/logout/saml2/slo"
response-url: "https://remoteidp2.slo.url"
binding: "POST"
assertingparty:
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"
assertingparty:
verification:
credentials:
- certificate-location: "path-to-other-verification-cert"
entity-id: "remote-idp-entity-id2"
sso-url: "https://remoteidp2.sso.url"
singlelogout:
url: "https://remoteidp2.slo.url"
reponse-url: "https://myapp/logout/saml2/slo"
binding: "POST"
对于 SAML2 注销,默认情况下,Spring Security 的Saml2LogoutRequestFilter
和Saml2LogoutResponseFilter
仅处理匹配的 URL/logout/saml2/slo
. 如果要自定义url
AP 发起的注销请求将发送到哪个或response-url
要使用其他模式,AP向其发送注销响应,您需要提供配置来处理该自定义模式。
例如,对于 servlet 应用程序,您可以添加自己的SecurityFilterChain
类似于以下内容:
@Configuration(proxyBeanMethods = false)
public class MySamlRelyingPartyConfiguration {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated();
http.saml2Login();
http.saml2Logout((saml2) -> saml2.logoutRequest((request) -> request.logoutUrl("/SLOService.saml2"))
.logoutResponse((response) -> response.logoutUrl("/SLOService.saml2")));
return http.build();
}
}
5. 春季课程
Spring Boot 为各种数据存储提供 Spring Session 自动配置。 在构建 Servlet Web 应用程序时,可以自动配置以下存储:
-
JDBC 公司
-
Redis
-
榛铸
-
Mongo数据库
servlet 自动配置取代了使用@Enable*HttpSession
.
构建响应式 Web 应用程序时,可以自动配置以下存储:
-
Redis
-
Mongo数据库
响应式自动配置取代了使用@Enable*WebSession
.
如果类路径上存在单个 Spring Session 模块,则 Spring Boot 会自动使用该 store 实现。
如果您有多个实现,则必须选择StoreType
您希望用于存储会话的。
例如,要使用 JDBC 作为后端存储,您可以按如下方式配置应用程序:
spring.session.store-type=jdbc
spring:
session:
store-type: "jdbc"
您可以通过将store-type 自none . |
每个商店都有特定的附加设置。 例如,可以自定义 JDBC 存储的表名称,如以下示例所示:
spring.session.jdbc.table-name=SESSIONS
spring:
session:
jdbc:
table-name: "SESSIONS"
要设置会话的超时,您可以使用spring.session.timeout
财产。
如果未使用 Servlet Web 应用程序设置该属性,则自动配置将回退到server.servlet.session.timeout
.
您可以使用以下命令来控制 Spring Session 的配置@Enable*HttpSession
(servlet) 或@Enable*WebSession
(反应)。
这将导致自动配置退出。
然后可以使用注释的属性而不是前面描述的配置属性来配置 Spring Session。
6. GraphQL 的弹簧
如果您想构建 GraphQL 应用程序,您可以利用 Spring Boot 对 Spring for GraphQL 的自动配置。
Spring for GraphQL 项目基于 GraphQL Java。
您将需要spring-boot-starter-graphql
至少是入门菜。
由于 GraphQL 与传输无关,因此您还需要在应用程序中有一个或多个额外的Starters才能通过 Web 公开您的 GraphQL API:
起动机 | 运输 | 实现 |
---|---|---|
|
HTTP |
弹簧 MVC |
|
Web套接字 |
用于 Servlet 应用程序的 WebSocket |
|
HTTP、Web套接字 |
Spring WebFlux |
|
TCP、Web套接字 |
Reactor Netty 上的 Spring WebFlux |
6.1. GraphQL 模式
Spring GraphQL 应用程序在启动时需要定义的模式。
默认情况下,您可以在src/main/resources/graphql/**
Spring Boot 将自动拾取它们。
您可以使用spring.graphql.schema.locations
文件扩展名为spring.graphql.schema.file-extensions
.
如果您希望 Spring Boot 检测该位置的所有应用程序模块和依赖项中的模式文件,
您可以设置spring.graphql.schema.locations 自"classpath*:graphql/**/" (请注意classpath*: 前缀)。 |
在以下部分中,我们将考虑此示例 GraphQL 架构,定义两种类型和两个查询:
type Query {
greeting(name: String! = "Spring"): String!
project(slug: ID!): Project
}
""" A Project in the Spring portfolio """
type Project {
""" Unique string id used in URLs """
slug: ID!
""" Project name """
name: String!
""" URL of the git repository """
repositoryUrl: String!
""" Current support status """
status: ProjectStatus!
}
enum ProjectStatus {
""" Actively supported by the Spring team """
ACTIVE
""" Supported by the community """
COMMUNITY
""" Prototype, not officially supported yet """
INCUBATING
""" Project being retired, in maintenance mode """
ATTIC
""" End-Of-Lifed """
EOL
}
默认情况下,将允许在架构上进行字段自省,因为 GraphiQL 等工具需要它。
如果您不希望公开有关模式的信息,您可以通过将spring.graphql.schema.introspection.enabled 自false . |
6.2. GraphQL 运行时配线
The GraphQL JavaRuntimeWiring.Builder
可用于注册自定义标量类型、指令、类型解析器、DataFetcher
s,等等。
您可以声明RuntimeWiringConfigurer
bean 以访问RuntimeWiring.Builder
.
Spring Boot 检测此类 bean 并将它们添加到 GraphQlSource 构建器中。
但是,通常情况下,应用程序不会实现DataFetcher
直接创建带注释的控制器。
Spring Boot 会自动检测@Controller
具有带注释的处理程序方法的类,并将它们注册为DataFetcher
s.
下面是问候语查询的示例实现,其中包含@Controller
类:
@Controller
public class GreetingController {
@QueryMapping
public String greeting(@Argument String name) {
return "Hello, " + name + "!";
}
}
;
@Controller
class GreetingController {
@QueryMapping
fun greeting(@Argument name: String): String {
return "Hello, $name!"
}
}
6.3. Querydsl 和 QueryByExample 存储库支持
Spring Data 提供对 Querydsl 和 QueryByExample 存储库的支持。
Spring GraphQL 可以将 Querydsl 和 QueryByExample 存储库配置为DataFetcher
.
Spring Data 存储库用@GraphQlRepository
并扩展以下之一:
-
QuerydslPredicateExecutor
-
ReactiveQuerydslPredicateExecutor
-
QueryByExampleExecutor
-
ReactiveQueryByExampleExecutor
被 Spring Boot 检测到并被视为候选者DataFetcher
用于匹配顶级查询。
6.4. 传输
6.4.1. HTTP 和 WebSocket
GraphQL HTTP 端点位于 HTTP POST/graphql
默认情况下。
路径可以通过以下方式进行自定义spring.graphql.path
.
Spring MVC 和 Spring WebFlux 的 HTTP 端点由RouterFunction 带有@Order 之0 .
如果您定义自己的RouterFunction beans,您可能希望添加适当的@Order 注释以确保它们正确排序。 |
默认情况下,GraphQL WebSocket 端点处于关闭状态。要启用它,请执行以下作:
-
对于 Servlet 应用程序,请添加 WebSocket Starters
spring-boot-starter-websocket
-
对于 WebFlux 应用程序,不需要额外的依赖项
-
对于两者,
spring.graphql.websocket.path
应用程序属性必须设置
Spring GraphQL 提供了一个 Web 拦截模型。
这对于从 HTTP 请求标头检索信息并在 GraphQL 上下文中设置它或从同一上下文获取信息并将其写入响应标头非常有用。
使用 Spring Boot,您可以声明WebInterceptor
bean 将其注册到 Web 传输中。
Spring MVC 和 Spring WebFlux 支持 CORS(跨域资源共享)请求。 CORS 是 GraphQL 应用程序的 Web 配置的关键部分,这些应用程序从使用不同域的浏览器访问。
Spring Boot 支持spring.graphql.cors.*
Namespace;下面是一个简短的配置示例:
spring.graphql.cors.allowed-origins=https://example.org
spring.graphql.cors.allowed-methods=GET,POST
spring.graphql.cors.max-age=1800s
spring:
graphql:
cors:
allowed-origins: "https://example.org"
allowed-methods: GET,POST
max-age: 1800s
6.4.2. RSocket
RSocket 也支持作为 WebSocket 或 TCP 之上的传输。
配置 RSocket 服务器后,我们可以使用spring.graphql.rsocket.mapping
.
例如,将该映射配置为"graphql"
意味着我们可以在发送请求时将其用作路由,并使用RSocketGraphQlClient
.
Spring Boot 自动配置一个RSocketGraphQlClient.Builder<?>
您可以注入组件的 bean:
@Component
public class RSocketGraphQlClientExample {
private final RSocketGraphQlClient graphQlClient;
public RSocketGraphQlClientExample(RSocketGraphQlClient.Builder<?> builder) {
this.graphQlClient = builder.tcp("example.spring.io", 8181).route("graphql").build();
}
@Component
class RSocketGraphQlClientExample(private val builder: RSocketGraphQlClient.Builder<*>) {
然后发送请求:
Mono<Book> book = this.graphQlClient.document("{ bookById(id: \"book-1\"){ id name pageCount author } }")
.retrieve("bookById")
.toEntity(Book.class);
val book = graphQlClient.document(
"""
{
bookById(id: "book-1"){
id
name
pageCount
author
}
}
"""
)
.retrieve("bookById").toEntity(Book::class.java)
6.5. 异常处理
Spring GraphQL 使应用程序能够注册一个或多个 SpringDataFetcherExceptionResolver
按顺序调用的组件。
异常必须解析为graphql.GraphQLError
对象,请参阅 Spring GraphQL 异常处理文档。
Spring Boot 会自动检测DataFetcherExceptionResolver
bean 并将它们注册到GraphQlSource.Builder
.
6.6. GraphiQL 和 Schema 打印机
Spring GraphQL 提供了基础设施,用于帮助开发人员使用或开发 GraphQL API。
Spring GraphQL 附带一个默认的 GraphiQL 页面,该页面在"/graphiql"
默认情况下。
默认情况下,此页面处于禁用状态,可以使用spring.graphql.graphiql.enabled
财产。
许多公开此类页面的应用程序更喜欢自定义生成。
默认实现在开发过程中非常有用,这就是为什么它会自动公开spring-boot-devtools
在开发过程中。
您还可以选择以文本格式公开 GraphQL 架构,网址为/graphql/schema
当spring.graphql.schema.printer.enabled
属性已启用。
7. Spring HATEOAS
如果您开发一个使用超媒体的 RESTful API,Spring Boot 会为 Spring HATEOAS 提供自动配置,该配置适用于大多数应用程序。
自动配置取代了使用@EnableHypermediaSupport
并注册了许多 bean,以简化构建基于超媒体的应用程序,包括LinkDiscoverers
(用于客户端支持)和ObjectMapper
配置为将响应正确地编组到所需的表示形式中。
这ObjectMapper
通过设置各种spring.jackson.*
属性,或者,如果存在,则通过Jackson2ObjectMapperBuilder
豆。
您可以使用以下命令来控制 Spring HATEOAS 的配置@EnableHypermediaSupport
.
请注意,这样做会禁用ObjectMapper
前面描述的自定义。
spring-boot-starter-hateoas 特定于 Spring MVC,不应与 Spring WebFlux 结合使用。
为了将 Spring HATEOAS 与 Spring WebFlux 一起使用,您可以添加对org.springframework.hateoas:spring-hateoas 以及spring-boot-starter-webflux . |
默认情况下,接受application/json
将收到一个application/hal+json
响应。
要禁用此行为,请spring.hateoas.use-hal-as-default-json-media-type
自false
并定义一个HypermediaMappingInformation
或HalConfiguration
配置 Spring HATEOAS 以满足您的应用程序及其客户端的需求。