逗号的博客

这里有一句格言,但我还没想好

通过前面几篇文章我们已经实现了将网关作为资源服务器并在网关实现认证和授权,其他服务将是一个纯粹的微服务,那么我们将问一个问题其他微服务如何获取到已授权用户的用户信息?

阅读全文 »

问题描述

通常授权服务是作为独立的服务进行部署的或者使用的是由其他服务商提供的服务。Spring Authorization Server 比较特殊,它既可以作为独立服务进行部署,就如在上一篇文章介绍的那样;又因为它是 Spring 体系下的一员,因此将它作为内部服务部署在 Gateway 后面又是完全可行的。但是在实践的过程中遇到了一点儿问题,下面是具体的问题描述。

阅读全文 »

我们将要实现这样一个微服务系统,将 Spring Cloud Gateway 作为 OAuth2 的资源服务器,在 Gateway 实现集中的统一的鉴权功能,各个微服务之间的调用不再单独鉴权。系统的规划如下表所示

系统 端口 说明
example-auth 9090 认证与授权服务
example-eureka 8761 服务注册中心
example-gateway 8080 服务网关
example-user 8081 用户服务
example-product 8082 商品服务
example-order 8083 订单服务
example-common - 公共模块

example-auth 是一个独立的服务,它不会向服务注册中心注册自己,其他的服务,包括 example-gateway,均需要向服务注册中心注册自己。

阅读全文 »

Spring Security OAuth 的生命周期已经结束,官方已经删除它的文档,现在推荐使用 Spring Authorization Server,但是还是有很多老项目在继续使用 Spring Security OAuth,学习它仍然是必要的。虽然官方文档已经删除了,但是找到了一篇较早时间的翻译 Spring Security OAuth2 开发指南,可以作为实践的基础。网络上也有很多如何使用它的文章,这篇文章是自己实践的一点记录。

阅读全文 »

备注:Knife4j 通过 com.github.xiaoymin.knife4j.spring.extension.Knife4jOpenApiCustomizer 类的 addOrderExtension 方法给 Tag 类应用 @ApiSupport 注解的 order 属性。

问题描述

在我们的一个项目中使用了 Knife4j 来显示 Swagger 的文档,具体的依赖如下

1
2
3
4
5
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
<version>4.3.0</version>
</dependency>

同时我们有两个 Controller,它们分别是

1
2
3
4
5
6
7
8
9
10
11
12
13
@RestController
@RequestMapping("/user")
@Tag(name = "用户管理")
public class UserController {
// 省略了...
}

@RestController
@RequestMapping("/role")
@Tag(name = "角色管理")
public class RoleController {
// 省略了...
}

默认情况下,在 Knife4j 的页面中“角色管理”显示在了“用户管理”的前面。我们期望“用户管理”显示在“角色管理”的前面。为了达到这个目的我们使用了 Knife4j 提供的注解 @ApiSupport,修改后的代码如下所示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@RestController
@RequestMapping("/user")
@Tag(name = "用户管理")
@ApiSupport(order = 1)
public class UserController {
// 省略了...
}

@RestController
@RequestMapping("/role")
@Tag(name = "角色管理")
@ApiSupport(order = 2)
public class RoleController {
// 省略了...
}

但是并没有什么效果,“用户管理”并没有显示在“角色管理”的前面,即 @ApiSupport 注解没有生效。

阅读全文 »

问题描述

我们有一个项目使用了 Knife4j,依赖的版本为

1
2
3
4
5
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
<version>4.3.0</version>
</dependency>

这个版本的 Knife4j 使用了 SpringDoc,其获取 Swagger 配置的地址为 /v3/api-docs/swagger-config,获取文档的地址为 /v3/api-docs,如果项目有多个分组,获取单个分组,比如分组“1-动物”,的文档的地址为 /v3/api-docs/1-动物

在项目部署时在项目的前面放置了一台 Nginx 反向代理服务器,配置了当访问路径为 /example 时会将请求转发到我们自己的项目

1
2
3
location /example/ {
proxy_pass http://localhost:8080/;
}

当访问 http://localhost/example/doc.html 时,Knife4j 能正常的访问 Swagger 的配置,即 http://localhost/example/v3/api-docs/swagger-config,但是无法访问 http://localhost/v3/api-docs/1-动物,因此无法打开 Knife4j 文档页面。

阅读全文 »

问题描述

首先创建一个 Spring Boot 项目,然后创建一个实体类 User,它有两个字段姓名 name 和生日 birthday,其中 birthday 的类型是 OffsetDataTime

1
2
3
4
5
@Data
public class User {
private String name;
private OffsetDateTime birthday;
}

最后创建对应的控制器 UserController,GET 请求的第二种形式将参数封装成了对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@RestController
@RequestMapping("/user")
public class UserController {
@GetMapping("/query1")
public OffsetDateTime query1(OffsetDateTime birthday) {
return birthday;
}

@GetMapping("/query2")
public User query2(User user) {
return user;
}

@PostMapping("/create")
public User create(@RequestBody User user) {
return user;
}
}

我们要实现接收前端传递过来的形如 yyyy-MM-dd HH:mm:ss 的字符串并自动将它转换为能够使用 OffsetDateTime 类型。

阅读全文 »

问题描述

在 SpringBoot 应用中可以使用以下几种方式来控制是否返回值为空的字段

  1. 在配置文件 application.yml 中配置全局自动忽略
    1
    2
    3
    spring:
    jackson:
    default-property-inclusion: NON_NULL
  2. 在类或字段上添加注解 @JsonInclude(JsonInclude.Include.NON_NULL)
  3. 实现一个 Jackson2ObjectMapperBuilderCustomizer 定制器
    1
    2
    3
    4
    5
    6
    @Bean
    public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() {
    return jacksonObjectMapperBuilder -> {
    jacksonObjectMapperBuilder.serializationInclusion(JsonInclude.Include.NON_NULL);
    };
    }
  4. 参考 JacksonObjectMapperConfiguration 自定义一个 ObjectMapper,覆盖默认的定义
    1
    2
    3
    4
    5
    6
    7
    8
    @Bean
    @Primary
    @ConditionalOnMissingBean
    public ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder) {
    ObjectMapper mapper = builder.createXmlMapper(false).build();
    mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
    return mapper;
    }

这 4 种方式要么是全局的要么是局部的,它们都是一次性的,设置后就不能改变,每次请求都会返回相同的结果。现在我们要实现根据请求参数中是否包含某个值动态的设置是否返回值为空的字段,具体的说我们要根据请求头中是否包含 X-Include-Non-Null 来控制是否返回值为空的字段。

阅读全文 »
0%