1、概览 Spring Security 有 2 个核心的组件:HttpSecurity 和 WebSecurity。
2、Spring Security Spring Security 是 Spring Framework 的扩展,是一个功能强大、高度可定制的安全框架。它为基于 Jakarta® EE 的企业软件应用提供全面的安全服务。它旨在处理身份验证、授权、防止常见安全漏洞等问题。
Spring Security 与两个重要组件 HttpSecurity 和 WebSecurity 紧密集成。
HttpSecurity 主要负责配置 HTTP 请求的安全,而 WebSecurity 则侧重于配置基于 Web 的安全。
Spring Security 的核心依赖于 AuthenticationManager、UserDetailsService 和 UserDetails 等基本组件。这些组件构成了 Spring Security 的基础。通过它们,我们可以实现身份认证、管理用户详情并有效定义访问控制。
3、HttpSecurity HttpSecurity 在配置 HTTP 请求的授权方面起到核心作用。它允许我们基于各种条件定义访问规则,包括 URL、HTTP 方法和用户角色,以确保对应用程序的不同部分进行安全访问。
链式方法调用是 HttpSecurity 的一个强大特性,它使我们能够流畅地表达安全配置。通过方法链,我们可以定义特定端点的安全行为,处理身份认证机制,并无缝设置自定义访问规则。这种方式让安全配置更加灵活和易于管理。
HttpSecurity 的一个关键特性是基于角色和权限进行访问配置。我们可以指定访问特定URL 所需的角色或权限。因此,我们可以对应用程序的安全性进行细粒度控制。
HttpSecurity 还可以实现平滑处理登录和注销功能。因此,我们可以配置自定义的登录页面、认证成功和失败的处理方式。它还允许我们实现自定义的注销行为,提升用户体验和安全性。
在使用 HttpSecurity 配置授权时,重要的是要解决安全问题。这些包括防止未经授权的访问、确保安全的身份认证机制、防止会话固定攻击,并防范常见的Web漏洞。
使用 HttpSecurity 来配置授权。
@Configuration @EnableWebSecurity public class HttpSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.
1、概览 本文将带你了解 Spring Boot 3.1 中引入的 ConnectionDetails 接口,用于编程式定义连接属性(可从外部服务中加载)。Spring Boot 提供了开箱即用的抽象,用于与远程服务集成,如关系型数据库、NoSQL 数据库、消息队列服务等
传统上,连接信息都是配置在 application.properties 文件中的。因此,如果需要从 AWS Secret Manager、Hashicorp Vault 等外部服务加载连接配置的话就比较麻烦。
为了解决这个问题,Spring Boot 引入了 ConnectionDetails。这个接口是空的,即标记接口。Spring 提供了该接口的子接口,如 JdbcConnectionDetails、CassandraConnectionDetails、KafkaConnectionDetails 等。它们可以在 Spring 配置类中实现和指定为 Bean。之后,Spring 将依赖这些配置 Bean 来动态检索连接属性,而不是静态的 application.properties 文件。
2、场景 把敏感的配置信息(如数据库)定义在 application.properties 文件中存在一定的安全隐患。最好的方式是使用专业的私密信息存储服务。
HashiCorp Vault 是一款企业级私密信息管理工具,用于安全地存储和访问敏感数据,例如API密钥、数据库凭据、密码等。支持多种认证方法和加密机制,并提供 API 和命令行界面,以便与应用程序和工具集成。还支持动态秘密生成、密钥轮换和审计日志等功能,使数据的管理和安全性更加便捷和可靠。
Spring Boot 从 Vault 中读取敏感信息的流程如下:
Spring Boot 通过 Secret Key 调用 Vault 服务来获取连接配置信息。然后,使用该配置和远程服务创建连接。
4、ConnectionDetails 通过 ConnectionDetails 接口,Spring Boot 应用可以自行发现连接细节(Connection Detail),而无需任何手动干预。注意,ConnectionDetails 优先于 application.properties 文件。不过,仍有一些非连接属性(如 JDBC 连接池大小)可以通过 application.properties 文件进行配置。
在前文 《在 Spring Boot 中通过 RequestBodyAdvice 统一解码请求体》 中,我们介绍了如何通过 RequestBodyAdvice 组件统一地解码客户端编码后的请求体。
有请求,就有响应,本文将会带你了解如何使用 ResponseBodyAdvice 来统一对响应体进行编码。
这种场景比较常见,常用于 APP 客户端和服务器之间的通信。为了避免中间人通过抓包直接获取到服务器的响应数据,所以会对数据进行加密(AES、3DES、RSA 等等)。
客户端获取到加密数据后,使用本地存储的密钥进行解密从而得到原始数据。由于密钥没有经过网络传输,意味着加密数据不会轻易被人破解。当然,密钥由于存储在客户端,需要通过混淆等手段保证它不会被轻易的破解、获取(这种安全话题超出了本文的范畴)!
ResponseBodyAdvice ResponseBodyAdvice spring mvc 提供的一个增强接口,用于在返回对象被 HttpMessageConverter 执行序列化之前对其进行一些自定义的操作。
public interface ResponseBodyAdvice <T> { boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType); @Nullable T beforeBodyWrite(@Nullable T body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response); } 这是一个带泛型 <T> 的接口,T 表示返回的对象类型,这决定了它会对哪些返回的对象生效。另外,它比 RequestBodyAdvice 简单,它只有 2 个方法。
supports:用于确定该实现类是否支持对响应体进行处理,通过 returnType 参数可以获取到 Controller 方法的返回类型等信息。 beforeBodyWrite:该方法在响应体写入之前被调用,在这个方法中可以通过参数获取到最终要响应给客户端的对象,我们可以对这个对象进行一些操作,最后返回修改后的对象。 接下来我们通过一个示例来了解如何使用 ResponseBodyAdvice。
1、简介 本文将带你了解如何使用 @WebServiceServerTest 为 Spring Boot 开发的 SOAP Web Service 编写集成测试。
2、测试 Spring Web Service 在 Spring Web Service 中,端点是服务器端 Service 实现的关键概念。专门的 @Endpoint 注解将类标记为 Web Service 端点。这些端点负责接收 XML 请求消息、调用所需的业务 Service 并将结果作为响应消息返回。
2.1、Spring Web Service 的测试支持 为了测试此类端点,我们可以通过传递所需的参数或模拟(Mock)来轻松创建单元测试。然而,这样做的主要缺点是无法实际测试通过网络发送的 XML 消息的内容。另一种方法是创建集成测试,以验证消息的 XML 内容。
Spring Web Service 2.0 引入了对此类端点集成测试的支持。提供这种支持的核心类是 MockWebServiceClient。它提供了一个 Fluent API,用于向 Spring Application Context 中配置的适当端点发送 XML 消息。此外,我们还可以设置响应预期、验证响应 XML,并对端点执行完整的集成测试。
不过,这需要调用整个 Application Context,从而减慢测试执行速度。这通常是不可取的,尤其是当我们希望为特定 Web Service 端点创建快速、隔离的测试时。
2.2、Spring Boot @WebServiceServerTest Spring Boot 2.6 通过 @WebServiceServerTest 注解扩展了 Web Service 测试支持。
在 Spring Boot 中,我们通过 @RequestMapping 注解来定义 API 端点,如下:
@RestController @RequestMapping public class DemoController { @GetMapping("/demo") public ResponseEntity<String> demo () { return ResponseEntity.ok("ok"); } } @GetMapping 是 @RequestMapping(method = RequestMethod.POST) 的快捷方式。
通过其 value 属性(path 属性的别名) 设置 API 的映射路径。
在本例中,路径就是:/demo。 启动应用,使用 cURL 进行访问。
$ curl localhost:8080/demo ok 一般来说,API 的路径不会轻易修改。而且路径是硬编码定义在注解中的,如果要修改的话,需要编辑源码重写编译打包。
但是,总有例外。如果我们想要灵活地修改 API 路径,那么可以在 @RequestMapping 中使用表达式从配置文件获取值。
先在配置文件(Properties / Yaml)中定义 API 路径:
app: router: demo: "/demo/v1" 然后,在 @RequestMapping 使用 ${app.router.demo} 获取该值,作为 API 的路径:
@RestController @RequestMapping public class DemoController { @GetMapping("${app.
1、概览 本文将带你了解几种模拟 HttpServletRequest 对象的方法。
首先,从 Spring Test 中的 MockHttpServletRequest 开始,这是一个功能齐全的模拟类型。然后,了解如何使用 Mockito 和 JMockit 这两个流行的模拟库进行测试。最后,了解如何使用匿名子类进行测试。
2、测试 HttpServletRequest 当我们要模拟客户端 request 信息(如 HttpServletRequest)来测试 Servlet 可能会有点麻烦,该接口定义了各种方法,需要进行实现。
要测试的目标 UserServlet 类,如下:
public class UserServlet extends HttpServlet { protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException { String firstName = request.getParameter("firstName"); String lastName = request.getParameter("lastName"); response.getWriter().append("Full Name: " + firstName + " " + lastName); } } 要对 doGet() 方法进行单元测试,需要模拟 request 和 response 参数,以模拟实际的运行时行为。
3、使用 Spring 的 MockHttpServletRequest Spring-Test 提供了一个功能齐全的类 MockHttpServletRequest,它实现了 HttpServletRequest 接口。
1、概览 OpenAPI 是一种与语言无关且独立于平台的规范,用于标准化 REST API。OpenAPI 使用户能够在不深入代码的情况下轻松理解 API。
Swagger-UI 根据 OpenAPI 规范生成可视化文档,帮助可视化和测试 REST API。
本文将带你了解如何在 Spring Boot 应用中使用 Springdoc-OpenAPI 生成 OpenAPI 文档、测试 REST API 并为 OpenAPI 配置 JWT Authentication(认证)。
2、Swagger-UI Swagger-UI 是 HTML、Javascript 和 CSS 文件的集合,可根据 OpenAPI 规范生成 UI 界面。
当 API 数量不断增加时,编写 OpenAPI 文档规范就变得非常具有挑战性。Springdoc-OpenAPI 可以帮助我们自动生成 OpenAPI 文档。
接下来,我们通过示例了解如何使用 Springdoc-OpenAPI 库自动生成 REST API 的 OpenAPI 文档,并使用 Swagger-UI 可视化这些 API。
2.1、依赖 首先,添加 Springdoc-OpenAPI 依赖。
<dependency> <groupId>org.springdoc</groupId> <artifactId>springdoc-openapi-ui</artifactId> <version>1.6.9</version> </dependency> 此依赖还将 Swagger-UI web-jar 添加到 Spring Boot 应用中。
在一些数据比较敏感或者对安全要求比较高的应用中,客户端提交给服务器的数据需要进行加密,服务器需要解密后才能获取到原始的请求数据。
在 Spring Boot 中,可以通过 RequestBodyAdvice 对请求体进行统一的解密处理,这对 Controller 来说是完全透明的,极大地提高了应用的可维护性。
RequestBodyAdvice 接口 这是由 spring mvc 提供的一个增强接口,用于在 HttpMessageConverter 读取请求体并把它转换为 Java 对象之前进行一些操作。
public interface RequestBodyAdvice { boolean supports(MethodParameter methodParameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType); HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) throws IOException; Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType); @Nullable Object handleEmptyBody(@Nullable Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?
简介 本文将带你了解 Spring Boot 应用中对 API 进行版本控制的重要性,以及不同的实现方式。
通过 API 版本控制,你可以对API进行更改,而不会破坏与现有客户端的兼容性。本文将介绍四种常见的版本控制方式:URI 版本控制、请求参数版本控制、自定义 Header 版本控制和内容协商(Accept Header)版本控制。
URI 版本控制 URI 版本控制涉及将版本号添加到 API 的 URL 中。这是一种直接而易于理解的方法。然而,它可能不是最优雅的方法,因为它可能导致URL变得很长。
示例:
@RestController @RequestMapping("/api/v1/users") public class UserControllerV1 { @GetMapping public ResponseEntity<List<User>> getUsers() { // 实现代码 } // 其他端点 } 请求参数版本控制 采用这种方法时,版本号会作为请求参数传递。这种方法比 URI 版本控制更省事,但对消费者来说可能不那么直观。
示例:
@RestController public class UserController { @GetMapping(value = "/api/users", params = "version=1") public ResponseEntity<List<User>> getUsersV1() { // 实现代码 } @GetMapping(value = "/api/users", params = "version=2") public ResponseEntity<List<User>> getUsersV2() { // 实现代码 } } 自定义 Header 版本控制 自定义 Header 版本控制涉及向 HTTP 请求添加一个自定义 Header,其中包含版本号。这种方法可以保持 URI 的清晰,但消费者必须记住要添加自定义 Header。
GraphQL 是啥? 根据其 官方文档,“GraphQL 是一种用于 API 的查询语言,也是一种服务器端运行时,可使用你为数据定义的类型系统来执行查询”。该语言由 Meta 公司开发并开源,目前由众多公司和个人社区共同维护。
GraphQL 包括 2 个主要部分:
Schema Definition Language(Schema 定义语言) - 使用 GraphQL SDL(Schema 定义语言)指定服务的 GraphQL Schema,有时也称为 GraphQL Schema 语言。 Query Language(查询语言) - 查询语言由Query、Mutation 和 Subscription 三部分组成 本文稍后会进行介绍。
GraphQL 有很多出色的功能,例如:
它实现了声明式数据获取,并提供了一个单一的端点,客户端可以准确地指定所需的信息,不再需要过度获取信息。 它提供开箱即用的验证和类型检查功能,你可以在执行前在 GraphQL 强类型系统中验证查询。 GraphQL 可以使 API 文档与 API 的更改保持同步。这对开发人员非常有益,因为他们无需花费太多时间来编写 API 文档。 GraphQL 通过在字段级别废除 API,消除了对版本控制的需求。旧字段可以在不影响现有查询的情况下从 Schema 中删除。 也有一些缺点:
GraphQL 需要客户端和服务器端更多的工具支持。这需要大量的前期付出。对于非常简单的 API 用例来说,这不是一个合适的替代方案。 GrpahQL 只有一个入口,默认使用 HTTP POST。这就妨碍了客户端 HTTP 缓存的充分使用,并使得实现缓存成为一项相对复杂的任务。 GraphQL Schema GraphQL 服务必须使用 Schema。GraphQL Schema 定义了如何从服务器请求和返回数据,并由 GraphQL SDL 控制。