GateWay具体的使用!!!

一、全局Token过滤器

在Spring Cloud Gateway中,实现全局过滤器的目的是对所有进入系统的请求或响应进行统一处理,比如添加日志、鉴权等。下面是如何创建一个全局过滤器的基本步骤:

步骤1: 创建过滤器类

首先,你需要创建一个实现了GatewayFilter接口或者继承AbstractGatewayFilterFactory类的过滤器类。这里以实现GatewayFilter接口为例,创建一个全局token过滤器。

java 复制代码
/*
 * Copyright (c) 2020, 2024,  All rights reserved.
 *
 */
package com.by.filter;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.jwt.JWTValidator;
import cn.hutool.jwt.signers.JWTSignerUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.RequestPath;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.List;

/**
 * <p>Project: spring-could-alibaba-root - TokenFilter</p>
 * <p>Powered by scl On 2024-04-26 17:41:24</p>
 * <p>描述:<p>
 *
 * @author 孙臣龙 [1846080280@qq.com]
 * @version 1.0
 * @since 17
 */
@Component
@Slf4j
public class TokenFilter implements GlobalFilter {

    @Value("${cn.smart.tokenx.key}")
    private String key;
    //设置白名单
    private static List<String> whiteList = CollUtil.newArrayList("/api/user/login", "/api/user/register");


    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();

        //判断如果包含白名单中的内容就放行
        RequestPath path = request.getPath();
        if (whiteList.contains(path)) {
            return chain.filter(exchange);
        }

        //从请求头中获取token
        List<String> tokens = request.getHeaders().get("token");
        if (ObjUtil.isEmpty(tokens)) {
            //设置状态码
            log.debug("token为空");
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            return response.setComplete();
        }
        //获取token
        String token = tokens.get(0);
        if (ObjUtil.isEmpty(token)) {
            log.debug("token为空");
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            return response.setComplete();
        }
        //全面增强
        Boolean b = false;
        try {
            JWTValidator.of(token).validateAlgorithm(JWTSignerUtil.hs256(key.getBytes())).validateDate();
            b = true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        if (!b) {
            log.debug("token无效");
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            return response.setComplete();
        }

        return chain.filter(exchange);
    }
}

步骤2: 注册全局过滤器

为了让Spring Cloud Gateway应用识别并使用这个过滤器,你通常需要在配置类中注册它。但是,由于我们使用了@Component注解,Spring会自动扫描并注册该Bean。

使用@Component或下面代码,二选一。

java 复制代码
@Configuration
public class FilterConfig {

    @Bean
    public TokenFilter tokenFilter() {
        return new TokenFilter();
    }
}

二、局部过滤器接口耗时

1.找规律

局部过滤器命名规则 XXXGatewayFilterFactory, 必须以GatewayFilterFactory结尾。

XML 复制代码
/*  注意名称约定
*   AddRequestHeaderGatewayFilterFactory    配置的时候写的是 AddRequestHeader
*   AddRequestParameterGatewayFilterFactory 配置的时候写的是 AddRequestParameter
*   LogTimeGatewayFilterFactory   配置的时候写什么? LogTime
* */

spring.cloud.gateway.routes[0].filters[0] = LogTime=moreThen,500

2.接口耗时过滤器

java 复制代码
/*
 * Copyright (c) 2020, 2024,  All rights reserved.
 *
 */
package com.by.filter;

import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.factory.AbstractNameValueGatewayFilterFactory;
import org.springframework.cloud.gateway.filter.factory.SetRequestHeaderGatewayFilterFactory;
import org.springframework.cloud.gateway.support.GatewayToStringStyler;
import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
import org.springframework.http.server.RequestPath;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.util.MultiValueMap;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

/**
 * <p>Project: spring-could-alibaba-root - LogTimeGatewayFilterFactory</p>
 * <p>Powered by scl On 2024-04-26 20:01:04</p>
 * <p>描述:<p>
 *
 * @author 孙臣龙 [1846080280@qq.com]
 * @version 1.0
 * @since 17
 */
@Component
@Slf4j
//局部过滤器命名规则 XXXGatewayFilterFactory, 必须以GatewayFilterFactory结尾。还需要配置文件中进行配置
public class LogTimeGatewayFilterFactory extends AbstractNameValueGatewayFilterFactory {
    public LogTimeGatewayFilterFactory() {
    }

    public GatewayFilter apply(final AbstractNameValueGatewayFilterFactory.NameValueConfig config) {
        return new GatewayFilter() {
            public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
                ServerHttpRequest request = exchange.getRequest();

                //获取配置文件中设置的参数
                String value = ServerWebExchangeUtils.expand(exchange, config.getValue());
                //将参数转换为int类型
                Integer time = Integer.valueOf(value);
                //设置开始时间
                long startTime = System.currentTimeMillis();
                //执行过滤器,并且记录请求耗时,通过调用then方法(类似于vue的then方法),记录请求耗时
                return chain.filter(exchange).then(Mono.fromRunnable(() -> {
                            long endTime = System.currentTimeMillis();
                            long costTime = endTime - startTime;
                            if (costTime  > time*1000) {
                                log.debug("请求地址:" + request.getPath() + ",请求耗时:" + costTime + "ms");
                            }
                        }));
            }

            public String toString() {
                return GatewayToStringStyler.filterToStringCreator(LogTimeGatewayFilterFactory.this).append(config.getName(), config.getValue()).toString();
            }
        };
    }
}

3.如何使用

三、全链路跟踪TraceId日志

1.创建全局过滤器,在请求头上带入traceId参数,穿透到下游服务.

java 复制代码
/*
 * Copyright (c) 2020, 2024,  All rights reserved.
 *
 */
package com.by.filter;

import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.List;

/**
 * <p>Project: spring-could-alibaba-root - TraceIdFilter</p>
 * <p>Powered by scl On 2024-04-26 21:10:19</p>
 * <p>描述:<p>
 *
 * @author 孙臣龙 [1846080280@qq.com]
 * @version 1.0
 * @since 17
 */
@Component
public class TraceIdFilter implements GlobalFilter {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        //获取请求头
        ServerHttpRequest request = exchange.getRequest();
        //判断请求头中有没有traceId
        List<String> list = request.getHeaders().get("traceId");
        //如果存在就不做处理
        if (ObjectUtil.isNotEmpty(list)){
            return chain.filter(exchange);
        }
        //不存在就生成一个traceId
        String treaId= IdUtil.simpleUUID();
        //在请求头中添加一个traceId
        ServerHttpRequest request1 = request.mutate().header("tracedId", treaId).build();
        //因为请求头发生了变化,所以需要重新构建一个ServerWebExchange
        ServerWebExchange exchange1 = exchange.mutate().request(request1).build();

        return chain.filter(exchange1);
    }
}

2.MDC原理

当请求来时生成一个traceId放在ThreadLocal里,然后打印时去取就行了。但在不改动原有输出语句的前提下自然需要日志框架的支持了。

MDC 介绍​ MDC(Mapped Diagnostic Context,映射调试上下文)是 log4j 和 logback 提供的一种方便在多线程条件下记录日志的功能。MDC 可以看成是一个与当前线程绑定的Map,可以往其中添加键值对。MDC 中包含的内容可以被同一线程中执行的代码所访问。当前线程的子线程会继承其父线程中的 MDC 的内容。当需要记录日志时,只需要从 MDC 中获取所需的信息即可。MDC 的内容则由程序在适当的时候保存进去。对于一个 Web 应用来说,通常是在请求被处理的最开始保存这些数据。

简而言之,MDC就是日志框架提供的一个InheritableThreadLocal,项目代码中可以将键值对放入其中,然后使用指定方式取出打印即可。

3.下游服务如何使用全链路跟踪Id

3.1在你要跟踪的模块中配置TraceId 过滤器

java 复制代码
/*
 * Copyright (c) 2020, 2024,  All rights reserved.
 *
 */
package com.by.filter;

import cn.hutool.core.util.IdUtil;
import org.slf4j.MDC;
import org.springframework.util.StringUtils;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
/**
 * <p>Project: spring-could-alibaba-root - TraceIdFliter</p>
 * <p>Powered by scl On 2024-04-26 21:20:15</p>
 * <p>描述:<p>
 *
 * @author 孙臣龙 [1846080280@qq.com]
 * @version 1.0
 * @since 17
 */
@WebFilter
public class TraceIdFliter implements Filter {

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        String traceId = httpRequest.getHeader("traceId");
        if (StringUtils.isEmpty(traceId)) {
            traceId = IdUtil.fastSimpleUUID();
        }
        MDC.put("traceId", traceId);

        try {
            chain.doFilter(request, response);
        } finally {
            MDC.clear();
        }

    }
}

3.2 启动类开启ServletComponentScan扫描。

java 复制代码
@SpringBootApplication

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

}

3.3 配置文件配置日志输出格式

##日志输出格式
logging.pattern.console=%d{yyyy-MM-dd HH:mm:ss.SSS} %clr(%-5level) %clr([%X{traceId}]) %clr(${PID:-}) --- %clr(%logger{50}) - %m%n


4.Openfeign扩展

java 复制代码
@Component
public class OpenFeignRequestInterceptor implements RequestInterceptor {
    @Override
    public void apply(RequestTemplate requestTemplate) {
        String traceId = MDC.get(TraceIdFilter.MDC_TRACE_ID);
        requestTemplate.header(TraceIdFilter.MDC_TRACE_ID, traceId);
    }
}


5.附(Kibana日志图标)

相关推荐
.生产的驴1 小时前
SpringCloud Gateway网关路由配置 接口统一 登录验证 权限校验 路由属性
java·spring boot·后端·spring·spring cloud·gateway·rabbitmq
天幕繁星14 小时前
docker desktop es windows解决vm.max_map_count [65530] is too low 问题
windows·elasticsearch·docker·docker desktop
百锦再16 小时前
详解基于C#开发Windows API的SendMessage方法的鼠标键盘消息发送
windows·c#·计算机外设
IT-民工2111017 小时前
Ansible剧本检测Windows防火墙状态
linux·运维·windows·自动化·ansible
菜鸟江多多20 小时前
【windows 下使用 tree】
windows
星晨羽20 小时前
esayExcel根据模板导出包含图片
java·开发语言·windows
开发者每周简报1 天前
当微软windows的记事本被AI加持
人工智能·windows·microsoft
命里有定数1 天前
windows工具 -- 使用rustdesk和云服务器自建远程桌面服务, 手机, PC, Mac, Linux远程桌面 (简洁明了)
linux·运维·服务器·windows·ubuntu·远程工作
菜菜-plus1 天前
分布式,微服务,SpringCloudAlibaba,nacos,gateway,openFeign
java·分布式·微服务·nacos·gateway·springcloud·openfeign
七月在野,八月在宇,九月在户1 天前
前端--> nginx-->gateway产生的跨域问题分析
前端·nginx·gateway