【第22章】Spring Cloud之Gateway集成Knife4j(下)


前言

上一章已经完成了在网关层集成Knife4j,这里来做一些Knife4j常用功能的使用。


一、访问页面加权控制

上一章我们通过访问地址,就直接访问到了文档界面,这样做是不安全的,我们需要添加基本的权限控制,官方已经提供了此功能,我们完成配置并开启即可。

1. 加权控制

在这里插入图片描述
注意basic的层级是在gateway下面的

knife4j:
  gateway:
    # ① 第一个配置,开启gateway聚合组件
    enabled: true
    # ② 第二行配置,设置聚合模式采用discover服务发现的模式
    strategy: discover
    discover:
      # ③ 第三行配置,开启discover模式
      enabled: true
      # ④ 第四行配置,聚合子服务全部为OpenAPI3规范的文档
      version: openapi3
    basic:
      enable: true
      # Basic认证用户名
      username: admin
      # Basic认证密码
      password: admin

2. 登录

在这里插入图片描述
在这里插入图片描述

二、生产环境如何屏蔽Knife4j、Swagger等Ui资源和接口

这是个老生常谈的问题了,开发环境对外开放接口和静态ui资源,没有问题,但是生产为了保护应用程序安全,建议对接口拦截屏蔽

1. 基于Spring Boot框架提供的@Conditional条件控制相关@Bean的生效

Spring Boot配置起来很方便,简单地配置开关即可

在这里插入图片描述

knife4j:
  gateway:
    # ① 第一个配置,开启gateway聚合组件
    enabled: false
    # ② 第二行配置,设置聚合模式采用discover服务发现的模式
    strategy: discover
    discover:
      # ③ 第三行配置,开启discover模式
      enabled: true
      # ④ 第四行配置,聚合子服务全部为OpenAPI3规范的文档
      version: openapi3
    basic:
      enable: true
      # Basic认证用户名
      username: admin
      # Basic认证密码
      password: admin

2. 效果

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

可以看出,我们的配置已经生效,生产环境上线时,
修改配置knife4j.gateway.enabled: false进行关闭,避免接口泄漏,造成安全问题

三、聚合个性化配置

这里我们对用户服务做个性化配置

1. 用户服务

1.1 引入依赖

<!-- https://mvnrepository.com/artifact/com.github.xiaoymin/knife4j-openapi3-jakarta-spring-boot-starter -->
<dependency>
    <groupId>com.github.xiaoymin</groupId>
    <artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
    <version>4.5.0</version>
</dependency>

1.2 Knife4j配置类

package org.example.user.config;

import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Contact;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.info.License;
import org.springdoc.core.customizers.GlobalOpenApiCustomizer;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;

/**
 * Create by zjg on 2024/8/4
 */
@Configuration
public class Knife4jConfiguration {
    @Value("${server.port}")
    private int port;
    /**
     * 根据@Tag 上的排序,写入x-order
     *
     * @return the global open api customizer
     */
    @Bean
    public GlobalOpenApiCustomizer orderGlobalOpenApiCustomizer() {
        return openApi -> {
            if (openApi.getTags()!=null){
                openApi.getTags().forEach(tag -> {
                    Map<String,Object> map=new HashMap<>();
                    map.put("x-order", new Random().nextInt(0,100));
                    tag.setExtensions(map);
                });
            }
            if(openApi.getPaths()!=null){
                openApi.addExtension("x-test123","333");
                openApi.getPaths().addExtension("x-abb",new Random().nextInt(1,100));
            }

        };
    }

    @Bean
    public OpenAPI customOpenAPI() {
        Info info = new Info();
        Contact contact = new Contact();
        contact.setName("zhangjg");
        contact.setUrl("http://localhost:"+port);
        contact.setEmail("zhangjg@gmail.com");
        info.setContact(contact);
        return new OpenAPI()
                .info(info
                        .title("用户应用程序接口文档")
                        .version("1.0")
                        .description( "用户服务")
                        .termsOfService("http://doc.xiaominfo.com")
                        .license(new License().name("Apache 2.0")
                                .url("http://doc.xiaominfo.com")));
    }
}

1.3 控制器

package org.example.user.controller;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.example.common.model.Result;
import org.example.common.util.JwtUtils;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Create by zjg on 2024/7/21
 */
@Tag(name = "用户模块")
@RestController
@RequestMapping("/user/")
public class UserController {
    List<String> users = List.of("admin");
    @Operation(summary = "用户是否存在")
    @RequestMapping("exist")
    public Boolean exist(@RequestParam("username") String username){
        boolean exist=false;
        if(users.contains(username)){
           exist=true;
        }
        return exist;
    }
    @Operation(summary = "用户登录")
    @RequestMapping("login")
    public Result<String> login(@RequestParam("username") String username, @RequestParam("password") String password){
        String message="用户名/密码不正确";
        String admin="admin";
        if(admin.equals(username)&&admin.equals(password)){
            Map<String, Object> claims=new HashMap<>();
            claims.put("username",username);
            return new Result<>(HttpStatus.OK.value(), "请求成功",JwtUtils.create(claims));
        }
        return Result.error(HttpStatus.UNAUTHORIZED.value(), message);
    }
}

2. 网关服务

现在是这么一个场景:用户服务太重要了,他们也要接入网关文档服务中,但是系统server.servlet.context-path配置还不能改,这么说大家可能不明白,直接看url区别吧
用户服务:http://localhost:9007/v3/api-docs
网关服务:http://localhost:9007/user/v3/api-docs

方案1
用户服务添加server.servlet.context-path=user,但是如果你的系统已经有很多内容了,不建议改,它的影响范围是ALL,会影响用户系统的所有请求。
方案2
用户服务添加接口/user/v3/api-docs,接收到请求之后,获取本机http://localhost:9007/v3/api-docs并将响应返回,相当于中转了一层
方案3 推荐 \color{#00FF00}{推荐} 推荐
其实我们的网关是很灵活的,这时候就需要网关层做动作适应它,先讲下思路:

  1. 排除的微服务user-serviceknife4j配置
  2. 定义新的路由和新的断言规则,指向user-service
  3. 手动配置user-service,聚合默认分组

2.1 排除用户服务

knife4j:
  gateway:
    # ① 第一个配置,开启gateway聚合组件
    enabled: true
    # ② 第二行配置,设置聚合模式采用discover服务发现的模式
    strategy: discover
    discover:
      # ③ 第三行配置,开启discover模式
      enabled: true
      # ④ 第四行配置,聚合子服务全部为OpenAPI3规范的文档
      version: openapi3
      # 需要排除的微服务(eg:网关服务) 
      excluded-services:
        - user-service

2.2 定义新用户服务路由

定义路由user-service1,并将请求http://localhost:9007/user/v3/api-docs去掉一层前缀,真实请求为http://localhost:9007/v3/api-docs,完美解决

spring:
  cloud:
    gateway:
      httpclient:
        connect-timeout: 1000
        response-timeout: 5s
      routes:
      - id: provider-service
        uri: lb://provider-service
        predicates:
        - Path=/provider/**
        - BlackRemoteAddr=192.168.1.1/24,127.0.0.1,192.168.0.104
      - id: consumer-service
        uri: lb://consumer-service
        predicates:
        - Path=/consumer/**,/echo-rest/**,/echo-feign/**
        filters:
        - BlackList=/consumer/hello1,/consumer/hello2
        metadata:
          optionName: "OptionValue"
          compositeObject:
            name: "value"
          iAmNumber: 1
          connect-timeout: 200
          response-timeout: 6000
      - id: user-service
        uri: lb://user-service
        predicates:
        - Path=/user/**
      - id: user-service1
        uri: lb://user-service
        predicates:
        - Path=/user-service/**
        filters:
        - StripPrefix=1

2.3 手动聚合分组

我们在knife4j.gateway下定义一套routes匹配我们刚才的路由即可

knife4j:
  gateway:
    # ① 第一个配置,开启gateway聚合组件
    enabled: true
    # ② 第二行配置,设置聚合模式采用discover服务发现的模式
    strategy: discover
    discover:
      # ③ 第三行配置,开启discover模式
      enabled: true
      # ④ 第四行配置,聚合子服务全部为OpenAPI3规范的文档
      version: openapi3
      # 需要排除的微服务(eg:网关服务) 
      excluded-services:
        - user-service
    # 子服务存在其他分组情况,聚合其他分组,只能手动配置
    routes:
      - name: 用户服务
        # 子服务存在其他分组情况,聚合其他分组
        url: /user-service/v3/api-docs?group=default
        # 服务名称(Optional)
        service-name: user-service
        # 路由前缀
        context-path: /
        # 排序
        order: 1
    basic:
      enable: true
      # Basic认证用户名
      username: admin
      # Basic认证密码
      password: admin

3. 效果展示

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述


总结

回到顶部

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值