在 Docker 中设置 Spring Boot Profile

1、简介 在容器化大行其道的今天,越来越多人选择把 Spring Boot 应用部署到 Docker 中。 本文将带你了解在 Docker 容器中启动 Spring Boot 应用时,如何设置其 Profile。 2、基本的 Dockerfile 一般来说,要容器化 Spring Boot 应用,只需提供一个 Dockerfile。 Spring Boot 应用的最小 Dockerfile,如下: FROM openjdk:11 COPY target/*.jar app.jar ENTRYPOINT ["java", "-jar", "/app.jar"] 然后,可以通过 docker build 来构建 Docker 镜像: docker build --tag=docker-with-spring-profile:latest . 接着,我们就可以通过 docker-with-spring-profile 镜像运行应用了: docker run docker-with-spring-profile:latest 你可以注意到,Spring Boot 应用以 “default” Profile 启动: 2022-04-22 22:34:25.268 INFO 1 --- [main] c.b.docker.spring.DemoApplication: Starting DemoApplication using Java 11.0.14.1 on ea8851bea75f with PID 1 (/app.

Spring Data JPA 级联删除单向关联

简介 本文将带你了解,当无法依赖 CascadeType 机制来将状态转换从父实体传播到子实体时,如何使用 Spring Data JPA 级联删除单向关联, Domain Model 假设我们的系统中有以下实体: Post 实体是该实体层次结构的根(root),子实体只使用单向 @ManyToOne 或 @OneToOne 关联,用于映射一个一对多或一对一表关系的底层外键。 Post root 实体如下: @Entity @Table(name = "post") public class Post { @Id private Long id; private String title; // getter/setter 方法省略 } 请注意,没有双向的 @OneToMany 或 @OneToOne 关联,可以允许我们从父级 Post 级联 DELETE 操作到 PostComment、PostDetails 或 PostTag 子实体。 PostComment 实体使用单向 @ManyToOne 关联映射 post_id 外键列: @Entity @Table(name = "post_comment") public class PostComment { @Id @GeneratedValue private Long id; @ManyToOne(fetch = FetchType.

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<?