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

在 Spring Boot 中禁用 Keycloak Security

1、概览 Keycloak 是一个开源的身份和访问管理解决方案。在测试阶段,禁用 Keycloak 可能有助于专注于业务测试。而且在测试环境中可能没有 Keycloak 服务器。 本文将带你了解如何禁用 Keycloak starter 的配置,以及如何在项目中启用 Spring Security 后如何对其进行修改。 2、在非 Spring Security 环境下禁用 Keycloak 我们首先来看看如何在不使用 Spring Security 的应用中禁用 Keycloak。 2.1、应用设置 首先,添加 spring-boot-starter-oauth2-client 依赖。 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-oauth2-client</artifactId> </dependency> 此外,还需要添加 spring-boot-starter-oauth2-resource-server 依赖。它将允许我们使用 Keycloak 服务器验证 JWT Token: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-oauth2-resource-server</artifactId> </dependency> 接下来,在 application.properties 中添加 Keycloak 服务器的配置: spring.security.oauth2.client.registration.keycloak.client-id=login-app spring.security.oauth2.client.registration.keycloak.authorization-grant-type=authorization_code spring.security.oauth2.client.registration.keycloak.scope=openid spring.security.oauth2.client.provider.keycloak.issuer-uri= http://localhost:8080/realms/SpringBootKeycloak spring.security.oauth2.client.provider.keycloak.user-name-attribute=preferred_username spring.security.oauth2.resourceserver.jwt.issuer-uri=http://localhost:8080/realms/SpringBootKeycloak 最后,添加 UserController 来检索用户: @RestController @RequestMapping("/users") public class UserController { @GetMapping("/{userId}") public User getCustomer(@PathVariable Long userId) { return new User(userId, "John", "Doe"); } } 2.

在 Spring Boot 中使用 Spring Security + JWT + MySQL 实现基于 Token 的身份认证

本文将会带你了解在 Spring Boot 中如何使用 Spring Security、JWT 和 MySQL 数据库实现基于 Token 的身份认证。 JWT (JSON Web Token)概览 JWT 是 JSON Web Token 的缩写,是一种安全地在各方之间传输信息的开放标准。它是一种紧凑、自包含的数据传输方法,通常用于客户端和服务器之间的数据传输。 JWT 通常用于认证和授权,服务器通过验证 JWT 中包含的数字签名来验证用户。 JWT 由三部分组成:Header、Payload 和 Signature(签名)。 Header 包含 Token 类型和 Token 签名算法的元数据。 Payload 包含关于被验证用户或实体的声明(Claim)或陈述。这些声明可包括用户 ID、用户名或电子邮件地址等信息。 Signature(签名)使用秘钥和 Header 及 Payload 生成,以确保 JWT 的完整性。 使用 JWT 的一个好处是它们是无状态的,这意味着服务器无需跟踪用户的身份认证状态。这可以提高可扩展性和性能。此外,JWT 可以在不同的域和服务中使用,只要它们共享相同的秘钥来验证签名即可。 Spring Security 概览 Spring Security 是一个提供身份认证、授权和防护常见攻击的框架。它为确保 Web 和响应式应用程序的安全提供一流的支持,是保护基于 Spring 的应用程序的事实标准。 Spring Security 用于保护 Web 应用程序、REST API 和微服务的安全,为身份认证和授权提供内置支持。 数据库表结构 添加 Maven 依赖 添加如下依赖到 Spring Boot 项目:

使用 @ExceptionHandler 处理 Spring Security 异常

1、概览 本文将会带你了解如何使用 @ExceptionHandler 和 @ControllerAdvice 全局处理 Spring Security 异常。 Controller Advice 是一种拦截器,常用于处理全局异常。 2、Spring Security 异常 Spring Security 核心异常(如 AuthenticationException 和 AccessDeniedException)属于运行时异常。由于这些异常是由 DispatcherServlet 后面的 Authentication Filter 在调用 Controller 方法之前抛出的,因此 @ControllerAdvice 无法捕获这些异常。 通过添加自定义 Filter 和构建响应体,可以直接处理 Spring Security 异常。要通过@ExceptionHandler 和 @ControllerAdvice 在全局级别处理这些异常,需要自定义 AuthenticationEntryPoint 的实现。AuthenticationEntryPoint 用于发送 HTTP 响应,要求客户端提供凭证。虽然已经有多个内置实现,但是我们仍然需要自己实现,以发送自定义响应。 首先,让我们看看如何在不使用 @ExceptionHandler 的情况下全局处理 Security 异常。 3、不使用 @ExceptionHandler Spring Security 异常是从 AuthenticationEntryPoint 开始的。让我们编写一个 AuthenticationEntryPoint 的实现,用于拦截 Security 异常。 3.1、配置 AuthenticationEntryPoint 实现 AuthenticationEntryPoint 并覆写 commence() 方法: @Component("customAuthenticationEntryPoint") public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint { @Override public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException { RestError re = new RestError(HttpStatus.

在 Spring Boot 中校验用户上传的图片文件

图片上传是现代应用中非常常见的一种功能,也是风险比较高的一个地方。恶意用户可能会上传一些病毒、木马。这些东西不仅严重威胁服务器的安全还浪费了带宽,磁盘等资源。所以,在图片上传的接口中,一定要对用户上传的文件进行严格的校验。 本文介绍了 2 种对图片文件进行验证的方法可供你参考。 文件后缀校验 通过文件后缀(也就是文件扩展名,通常用于表示文件的类型),进行文件类型校验这是最常见的做法。 图片文件的后缀类型有很多,常见的只有:jpg、jpeg、gif、png、webp。我们可以在配置或者代码中定义一个“允许上传的图片后缀”集合,用于校验用户上传的图片文件。 package cn.springdoc.demo.web.controller; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Set; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; @RestController @RequestMapping("/upload") public class UploadController { // 允许上传的图片类型的后缀集合 static final Set<String> imageSuffix = Set.of("jpg", "jpeg", "gif", "png", "webp"); @PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE) public ResponseEntity<String> upload (@RequestParam("file") MultipartFile file ) throws IllegalStateException, IOException{ // 文件的原始名称 String fileName = file.getOriginalFilename(); if (fileName == null) { return ResponseEntity.

Spring 的 beanName 设置可能会导致代理失败

一些使用小细节就是在不断的源码探索中逐步发现的,今天就来和大家研究一下通过 beanName 的设置,可以让一个 bean 拒绝被代理的问题! 1. 代码实践 假设我有如下一个切面: @Aspect @EnableAspectJAutoProxy @Component public class LogAspect { @Pointcut("execution(* org.javaboy.demo.service.*.*(..))") public void pc() { } @Before("pc()") public void before(JoinPoint jp) { String name = jp.getSignature().getName(); System.out.println(name + " 方法开始执行了..."); } } 这个切面要拦截的方法是 org.javaboy.demo.service 包下的所有类的所有方法,现在,这个包下有一个 BookService 类,内容如下: @Service("org.javaboy.demo.service.BookService.ORIGINAL") public class BookService { public void hello() { System.out.println("hello bs"); } } 这个 BookService 的 beanName 我没有使用默认的 beanName,而是自己配置了一个 beanName,这个 beanName 的配置方式是 类名的完整路径 + .ORIGINAL。 当我们按照这样的规则给 bean 取名之后,那么即使当前 bean 已经包含在切点所定义的范围内,这个 bean 也不会被代理了。

在 Spring Cloud Gateway 中修改响应体

1、简介 本文将带你了解如何在 Spring Cloud Gateway 中读取、修改响应体,然后再响应给客户端。 2、Spring Cloud Gateway 快速回顾 Spring Cloud Gateway(简称 SCG)是 Spring Cloud 系列中的一个子项目,它提供了一个构建在响应式 Web 栈之上的 API 网关。关于它的更多详细信息和用法,你可以参考 官方文档。 设计 API Gateway 解决方案时经常出现的一种特殊使用场景:如何在将后端响应的 Body 发送回客户端之前对其进行处理? 下面列出了一些可能会用到这种功能的场景: 保持与现有客户端的兼容性,同时允许后台不断迭代 需要屏蔽响应中的某些敏感字段(脱敏) 实现这个需求,只需要实现一个 Filter 来处理后台响应即可。Filter 是 SCG 的核心概念。 Filter 组件创建后就可以将其应用于任何已声明的路由(Route)。 3、实现数据过滤 Filter 创建一个简单的 Filter 来屏蔽 JSON 响应中的某些值。 例如,给定的 JSON 有一个名为 “ssn” 的字段: { "name" : "John Doe", "ssn" : "123-45-9999", "account" : "9999888877770000" } 我们希望用一个固定的值进行替换,从而防止数据泄漏: { "name" : "John Doe", "ssn" : "****", "account" : "9999888877770000" } 3.

使用 Spring 注解实例化同一个类的多个 Bean

1、概览 Spring IoC 容器创建和管理 Spring Bean,这些 Bean 是应用的核心。创建一个 Bean 实例与从普通的 Java 类创建对象相同。然而,生成多个相同类的 Bean 可能会比较麻烦一点。 本文将带你了解如何在 Spring 中使用注解创建同一个类的多个 Bean。 2、使用 Java 配置 这是使用注解创建多个同类 Bean 的最简单易行的方法。 举一个简单的例子。我们有一个 Person 类,它有两个字段:firstName 和 lastName: public class Person { private String firstName; private String lastName; public Person(String firstName, String secondName) { super(); this.firstName = firstName; this.lastName = secondName; } @Override public String toString() { return "Person [firstName=" + firstName + ", secondName=" + lastName + "]"; } } 接下来,构建一个名为 PersonConfig 的配置类,并在其中定义 Person 类的多个 Bean:

使用 Testcontainers 测试 Redis

1、概览 Testcontainers 是一个用于创建临时 Docker 容器进行单元测试的 Java 库。当我们想要避免使用实际服务器进行测试时,它非常有用。 本文将会带你了解如何在 Spring Boot 中使用 Testcontainers 测试 Redis。 2、项目设置 使用任何测试容器的首要前提是在运行测试的机器上安装 Docker。 安装好 Docker 后,就可以开始设置 Spring Boot 应用了。 在此应用中,我们将设置一个 Redis Hash、一个 Repository 和一个使用 Repository 与 Redis 交互的 Service。 2.1、依赖 添加所需的 spring-boot-starter-test 和 spring-boot-starter-data-redis 依赖。 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> 接着,还要添加 Testcontainers 依赖: <dependency> <groupId>org.testcontainers</groupId> <artifactId>testcontainers</artifactId> <version>1.17.2</version> <scope>test</scope> </dependency> 2.2、配置 在 application.properties 文件中添加 Redis 连接的详细信息: spring.redis.host=127.0.0.1 spring.redis.port=6379 3、应用设置 我们要创建一个小型应用,向 Redis 数据库读写 Product(产品)。 3.1、Entity 创建 Product 类。