Spring Security 中的 HttpSecurity 与 WebSecurity

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.

Spring Boot 3.1 中的 ConnectionDetails

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 中通过 ResponseBodyAdvice 统一编码响应体

在前文 《在 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。

使用 @WebServiceServerTest 进行 Spring Web Service 集成测试

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 测试支持。

在 Properties / Yaml 中配置 @RequestMapping 的 value 值

在 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.

如何 Mock(模拟)HttpServletRequest

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 接口。

给 OpenAPI 配置 JWT Authentication(认证)

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 统一解码请求体

在一些数据比较敏感或者对安全要求比较高的应用中,客户端提交给服务器的数据需要进行加密,服务器需要解密后才能获取到原始的请求数据。 在 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 的版本控制

简介 本文将带你了解 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。

使用 Spring Boot 创建 GraphQL API

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 控制。