SpringCloud Gateway基于JWT整合Swagger2聚合微服务系统API文档

需求

1、有两个微服务user-service和order-service都需要使用swagger2生成接口文档,后期还有其他微服务。如果每一个都去访问http://ip:port/swagger-ui.html会比较麻烦。
2、后端接口访问都是需要携带token令牌才能进行请求访问的,如果每一个微服务都需要做请求拦截,代码冗余繁琐,后期不便于维护。

想达到的效果: 统一访问gateway网关http://localhost:10010/swagger-ui.html地址,在网关中统一管理所有微服务的swagger文档,访问用户管理的单点登录获取token,然后携带token去访问微服务的其他接口。

端口如下:
在这里插入图片描述
页面效果:
user-service用户微服务的接口文档
在这里插入图片描述

切换order-service订单微服务的接口文档
在这里插入图片描述

解决

配置swagger2

pom

<dependency>
    <groupId>com.spring4all</groupId>
    <artifactId>swagger-spring-boot-starter</artifactId>
    <version>1.9.1.RELEASE</version>
</dependency>

swagger配置类

这里和springboot使用swagger一样

/**
 * Swagger2配置信息
 */
@Configuration
@EnableSwagger2
public class Swagger2Config {

//    @Value("${swagger.enable}")
//    private boolean swaggerEnable;
    //是否允许显示swagger。此值可在application.yml中设定。
    //作为开关,可在生产环境和开发环境打开或关闭,简便易行。

    @Bean
    public Docket webApiConfig(){
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(webApiInfo())
                //.enable(swaggerEnable)
                .select()
                // 针对方法注解ApiOperation生成接口文档api
                .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
                .paths(PathSelectors.any())
                .build();

    }
    private ApiInfo webApiInfo(){
        return new ApiInfoBuilder()
                .title("网站-API文档")
                .description("本文档描述了网站微服务接口定义")
                .version("1.0")
                .contact(new Contact("封于修", "http://zysheep.cn", "zysheep@126.com"))
                .build();
    }
}

这里我把swagger2抽成了一个公共的组件,微服务需要就引入坐标依赖,并在启动类上使用@ComponentScan注解扫描组件到ioc容器中即可使用swagger2
在这里插入图片描述

order-service测试使用

这里使用@ComponentScan(basePackages = {"cn.zysheep"})扫描swagger注册为容器中的Bean
在这里插入图片描述
在这里插入图片描述

配置gateway网关

我的环境

springbootspringcloudspring-cloud-alibaba
2.3.9.RELEASEHoxton.SR102.2.6.RELEASE

在这里插入图片描述

pom

  <!--网关-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!--nacos服务发现依赖-->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--swagger依赖 Spring Cloud Gateway整合Swagger聚合微服务系统API文档-->
<dependency>
    <groupId>com.spring4all</groupId>
    <artifactId>swagger-spring-boot-starter</artifactId>
    <version>1.9.1.RELEASE</version>
</dependency>
<dependency>
    <groupId>cn.zysheep</groupId>
    <artifactId>commons-util</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>

yml

server:
  port: 10010 # 网关端口


spring:
  application:
    name: gateway
  cloud:
    nacos:
      server-addr: localhost:8848
      discovery:
        namespace: 4f64927e-a2a7-4e2f-b657-3da7c57eb008
    gateway:
      routes: # 网关路由配置
        - id: user-service # 路由id,自定义,只要唯一即可
          # uri: http://127.0.0.1:8081 # 路由的目标地址 http就是固定地址
          uri: lb://user-service # 路由的目标地址 lb就是负载均衡,后面跟服务名称
          predicates: # 路由断言,也就是判断请求是否符合路由规则的条件
            - Path=/user/** # 这个是按照路径匹配,只要以/user/开头就符合要求
          filters:
            - StripPrefix=1
        - id: order-service
          uri: lb://order-service
          predicates:
            - Path=/order/**
          filters:
            - StripPrefix=1
logging:
  level:
    cn.zysheep.gateway: debug

GatewayApplication启动类

@SpringBootApplication
public class GatewayApplication {
    public static void main(String[] args) {
        SpringApplication.run(GatewayApplication.class,args);
    }
}

SwaggerProvider

配置SwaggerProvider,获取Api-doc,即SwaggerResources。

@Component
@Primary
@AllArgsConstructor
public class SwaggerProvider implements SwaggerResourcesProvider {
    public static final String API_URI = CommonConstants.SWAGGER_URL;
    private final RouteLocator routeLocator;
    private final GatewayProperties gatewayProperties;


    @Override
    public List<SwaggerResource> get() {
        List<SwaggerResource> resources = new ArrayList<>();
        List<String> routes = new ArrayList<>();
        //取出gateway的route
        routeLocator.getRoutes().subscribe(route -> routes.add(route.getId()));
        //结合配置的route-路径(Path),和route过滤,只获取有效的route节点
        gatewayProperties.getRoutes().stream().filter(routeDefinition -> routes.contains(routeDefinition.getId()))
                .forEach(routeDefinition -> routeDefinition.getPredicates().stream()
                        .filter(predicateDefinition -> ("Path").equalsIgnoreCase(predicateDefinition.getName()))
                        .forEach(predicateDefinition -> resources.add(swaggerResource(routeDefinition.getId(),
                                // 1、公共路径使用服务名的http://localhost:10010/user/v2/api-docs
//                                predicateDefinition.getArgs().get(NameUtils.GENERATED_NAME_PREFIX + "0")
//                                        .replace("/**", API_URI)))));
                                // 2、公共路径使用服务名的http://localhost:10010/user-service/v2/api-docs
                                "/"+routeDefinition.getId()+API_URI ))));

        return resources;
    }
    private SwaggerResource swaggerResource(String name, String location) {
        SwaggerResource swaggerResource = new SwaggerResource();
        swaggerResource.setName(name);
        swaggerResource.setLocation(location);
        swaggerResource.setSwaggerVersion("2.0");
        return swaggerResource;
    }
}

SwaggerHandler

因为Gateway里没有配置SwaggerConfig,而运行Swagger-ui又需要依赖一些接口,所以我的想法是自己建立相应的swagger-resource端点。

@RestController
@RequestMapping("/swagger-resources")
public class SwaggerHandler {
    @Autowired(required = false)
    private SecurityConfiguration securityConfiguration;
    @Autowired(required = false)
    private UiConfiguration uiConfiguration;
    private final SwaggerResourcesProvider swaggerResources;

    @Autowired
    public SwaggerHandler(SwaggerResourcesProvider swaggerResources) {
        this.swaggerResources = swaggerResources;
    }


    @GetMapping("/configuration/security")
    public Mono<ResponseEntity<SecurityConfiguration>> securityConfiguration() {
        return Mono.just(new ResponseEntity<>(
                Optional.ofNullable(securityConfiguration).orElse(SecurityConfigurationBuilder.builder().build()), HttpStatus.OK));
    }

    @GetMapping("/configuration/ui")
    public Mono<ResponseEntity<UiConfiguration>> uiConfiguration() {
        return Mono.just(new ResponseEntity<>(
                Optional.ofNullable(uiConfiguration).orElse(UiConfigurationBuilder.builder().build()), HttpStatus.OK));
    }

    @GetMapping("")
    public Mono<ResponseEntity> swaggerResources() {
        return Mono.just((new ResponseEntity<>(swaggerResources.get(), HttpStatus.OK)));
    }

}

SwaggerHeaderFilter

@Component
public class SwaggerHeaderFilter extends AbstractGatewayFilterFactory {
    private static final String HEADER_NAME = "X-Forwarded-Prefix";

    @Override
    public GatewayFilter apply(Object config) {
        return (exchange, chain) -> {
            ServerHttpRequest request = exchange.getRequest();
            String path = request.getURI().getPath();
            if (!StringUtils.endsWithIgnoreCase(path, SwaggerProvider.API_URI)) {
                return chain.filter(exchange);
            }
            String basePath = path.substring(0, path.lastIndexOf(SwaggerProvider.API_URI));
            ServerHttpRequest newRequest = request.mutate().header(HEADER_NAME, basePath).build();
            ServerWebExchange newExchange = exchange.mutate().request(newRequest).build();
            return chain.filter(newExchange);
        };
    }
}

AuthorizeFilter全局过滤器

Token工具类JWTUtil

@Order(-1)
@Component
@Slf4j
public class AuthorizeFilter implements GlobalFilter {

    /**
     *  处理当前请求,有必要的话通过{@link GatewayFilterChain}将请求交给下一个过滤器处理
     *
     * @param exchange 请求上下文,里面可以获取Request、Response等信息
     * @param chain 用来把请求委托给下一个过滤器
     * @return {@code Mono<Void>} 返回标示当前过滤器业务结束
     */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        log.debug("请求:  {}", exchange.getRequest().getURI().getPath());
        log.info("---------enter gateway interceptor--------");
        String url = exchange.getRequest().getURI().getPath();
        log.info("url---------------{}", url);
        //登录直接放行
        if (StringUtils.contains(url, CommonConstants.LOGIN_URL)) {
            log.info("     login .............. ");
            return chain.filter(exchange);
        }else if(StringUtils.contains(url, CommonConstants.SWAGGER_URL)){//swagger直接放行
            log.info("     swagger2 .............. ");
            return chain.filter(exchange);
        } else {

            //获取token
            ServerHttpRequest request = exchange.getRequest();
            String token = request.getHeaders().getFirst("token");
            //验证token
            Map<String, Object> tokenMap = JWTUtil.parseToken(token);
            log.info("-----{}", tokenMap);
            if (ObjectUtils.isEmpty(tokenMap.get("ERR_CODE"))) {
                request = exchange.getRequest().mutate().headers(httpHeaders -> {
                    httpHeaders.add("tokenMap", JSON.toJSONString(tokenMap));
                }).build();
                ServerWebExchange build = exchange.mutate().request(request).build();
                return chain.filter(build);
            } else {
                return errorInfo(exchange, tokenMap.get("ERR_MSG").toString(), 500);
            }
        }
    }

    /**
     * 返回response
     *
     * @param exchange
     * @param message  异常信息
     * @param status   data中的status
     * @return
     */
    public static Mono<Void> errorInfo(ServerWebExchange exchange, String message, Integer status) {
        // 自定义返回格式
        Map<String, Object> resultMap = new HashMap<>(8);
        resultMap.put("code", status);
        resultMap.put("msg", StringUtils.isBlank(message) ? "服务异常!" : message);
        resultMap.put("data", null);
        return Mono.defer(() -> {
            byte[] bytes;
            try {
                bytes = new ObjectMapper().writeValueAsBytes(resultMap);
            } catch (JsonProcessingException e) {
                log.error("网关响应异常:", e);
                throw new RuntimeException("信息序列化异常");
            } catch (Exception e) {
                log.error("网关响应异常:", e);
                throw new RuntimeException("写入响应异常");
            }
            ServerHttpResponse response = exchange.getResponse();
            response.getHeaders().add("Content-Type", MediaType.APPLICATION_JSON_UTF8.toString());
            DataBuffer buffer = response.bufferFactory().wrap(bytes);
            return response.writeWith(Flux.just(buffer));
        });
    }
}

测试

测试流程:

  1. 访问gateway网关http://localhost:10010/swagger-ui.html
  2. 访问用户微服务的单点登录获取token值
  3. 复制token,访问订单微服务的订单管理查询订单

1、访问gateway网关http://localhost:10010/swagger-ui.html
在这里插入图片描述
2、访问用户微服务的单点登录获取token值
在这里插入图片描述

3、复制token,访问订单微服务的订单管理查询订单
在这里插入图片描述
成功响应结果
在这里插入图片描述

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

李熠漾

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值