[Spring Cloud] GateWay自定义过滤器/结合Nacos服务注册中心

✨✨个人主页:沫洺的主页

📚📚系列专栏: 📖 JavaWeb专栏📖 JavaSE专栏 📖 Java基础专栏📖vue3专栏 

                           📖MyBatis专栏📖Spring专栏📖SpringMVC专栏📖SpringBoot专栏

                           📖Docker专栏📖Reids专栏📖MQ专栏📖SpringCloud专栏     

💖💖如果文章对你有所帮助请留下三连✨✨

衔接上篇: [Spring Cloud] GateWay服务网关_沫洺的博客-CSDN博客

🍁自定义全局过滤器

官方定义全局过滤器示例

接下来仿照示例创建我们自己的全局过滤器 

需求: 进行token验证(网关可以理解为一个交换机,有request和response),将原来的request通过验证并解析,过滤后构建新的request来正常调用后方的具体接口

子模块:spring-cloud-gateway

pom添加个hutool依赖

        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
        </dependency>

application.properties

spring.cloud.gateway.routes[0].id = route1
spring.cloud.gateway.routes[0].predicates[0] = Path=/test/**
spring.cloud.gateway.routes[0].filters[0] = StripPrefix=1
spring.cloud.gateway.routes[0].uri = http://httpbin.org

解析对象UserInfo

@Data
public class UserInfo {
    private Integer userId;
    private String nickName;
}

全局过滤器 CustomGlobalFilter

@Component
public class CustomGlobalFilter implements GlobalFilter, Ordered {

    //密钥
    private static final String TOKEN_SECRET="moming123";
    @Bean
    public GlobalFilter customFilter(){
        return new CustomGlobalFilter();
    }
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();
        //获取请求头Authorization存放的JWT结构的token数据
        List<String> tokenList = request.getHeaders().get("Authorization");
        //如果为空,报401异常
        if(ObjectUtil.isEmpty(tokenList)){
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            return response.setComplete();
        }
        //不为空则获取token
        String token = tokenList.get(0);
        boolean isTrue = false;
        try {
            //验证密钥
            isTrue = JWTUtil.verify(token, TOKEN_SECRET.getBytes());
        } catch (Exception e) {
            e.printStackTrace();
        }
        //不匹配报401
        if(isTrue==false){
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            return response.setComplete();
        }
        //解析token
        final JWT jwt = JWTUtil.parseToken(token);
        //获取JWT负载部分,转成UserInfo对象
        //System.out.println(jwt.getPayload().toString());
        UserInfo userInfo = JSONUtil.toBean(jwt.getPayloads().toString(), UserInfo.class);
        Integer userId = userInfo.getUserId();
        //将userId通过header传到后端
        //原来的request是只读的,需要通过mutate复制一份新的request2 并添加请求头
        ServerHttpRequest request2 = request.mutate().header("userId", String.valueOf(userId)).build();

        //可以正常调用后方的具体接口
        //交换机也是不能改动所以复制一份,并发送新的request2
        return chain.filter(exchange.mutate().request(request2).build());
    }

    @Override
    public int getOrder() {
        //越小优先级越高
        return -1;
    }
}

通过启动类生成一个JWT结构的token

@SpringBootApplication
public class GatewayApp {

    public static void main(String[] args) {
        SpringApplication.run(GatewayApp.class, args);

        Map<String,Object> map = new HashMap<>();
        map.put("userId", 1);
        map.put("nickName", "moming");
        map.put("expire_time", System.currentTimeMillis()+1000*60*60*24*5);

        String token = JWTUtil.createToken(map, "moming123".getBytes());
        System.out.println(token);
    }
}

运行

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJuaWNrTmFtZSI6Im1vbWluZyIsImV4cGlyZV90aW1lIjoxNjY5NjMxMDcxNDE4LCJ1c2VySWQiOjF9.wJqasnKRQwdB-u-pm6hzqcTlfRQwX2ioHHmROspBt7Q

token错误场景

JWT回顾:[SpringBoot-vue3]用户登录实现JWT单点登录/ThreadLocal保存用户信息_沫洺的博客-CSDN博客参考博文: JSON Web Token 入门教程

🍂自定义网关(局部)过滤器

局部过滤器是只针对某个网关(路由)去生效

全局过滤器实现的是GlobalFilter,而局部过滤器实现的是GatewayFilter

定义一个局部过滤器

需求: 呈现网关调用接口耗时

这里网关调用接口服务时是异步的,比如说网关通过某个线程1去访问a服务,访问结束后返回数据时走的又是另外一个线程2,所以是异步的

那么我们要统计耗时开始时间就不能通过线程1来统计(原因是线程1可能去调用其他服务,会设置新的开始时间,),但交换机只有一个,所以可以给交换机设置一个属性开始时间戳,交换机开始作用到调用结束就是网关调用接口的耗时

@Slf4j
public class LoggerGatewayFilter implements GatewayFilter, Ordered {

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        //开始调用
        //设置开始时间戳
        exchange.getAttributes().put("start",System.currentTimeMillis());
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();
        //过滤之后then异步执行一个方法
        return chain.filter(exchange).then(Mono.fromRunnable(()->{
            //运行结束
            long end = System.currentTimeMillis();

            long start = exchange.getAttribute("start");
            long msTime = end - start;
            //获取接口
            String rawPath = request.getURI().getRawPath();
            log.info("接口:{} -> 耗时:{}",rawPath,msTime);
        }));
    }

    @Override
    public int getOrder() {
        return -1;
    }
}

但是局部过滤器不能像全局过滤器那样定义完放到springioc中就可以使用,要通过工厂来将我们自定义的局部过滤器生产出来,而且既然是局部的,那必然要配置指定的网关去生效,来达到按需加载的效果

配置时有个命名规则

参考内置的局部过滤器的类的命名方式及配置的命名方式

AddRequestHeaderGatewayFilterFactory======>AddRequestHeader

AddRequestParameterGatewayFilterFactory======>AddRequestParameter

所以我们自定义时的类名要加GatewayFilterFactory,配置时用前面部分即可

比如我们自定义的LoggerGatewayFilterFactory ======>Logger

@Component
public class LoggerGatewayFilterFactory extends AbstractGatewayFilterFactory<Object> {
    @Override
    public GatewayFilter apply(Object config) {
        return new LoggerGatewayFilter();
    }
}

配置

spring.cloud.gateway.routes[0].id = route1
spring.cloud.gateway.routes[0].predicates[0] = Path=/test/**
spring.cloud.gateway.routes[0].filters[0] = StripPrefix=1
spring.cloud.gateway.routes[0].filters[1] = Logger
spring.cloud.gateway.routes[0].uri = http://httpbin.org

注释掉自定义全局过滤器相关内容,运行访问

🌾结合Nacos服务注册中心

注释掉局部过滤器相关内容

调用a服务

添加依赖

        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
            <!--<version>2.2.1.RELEASE</version>-->
        </dependency>

配置nacos

#与nacos服务注册中心结合
spring.cloud.gateway.routes[1].id = route_nacos
spring.cloud.gateway.routes[1].predicates[0] = Path=/nacos-a/**
spring.cloud.gateway.routes[1].filters[0] = StripPrefix=1
#前置过滤器
spring.cloud.gateway.routes[1].filters[1] = AddRequestHeader=name,moming
spring.cloud.gateway.routes[1].uri = lb://nacos-a

启动nacos服务器

修改子模块spring-cloud-nacos-a

@RestController
public class UserServiceController implements IUserService {
    @Value("${server.port}")
    private String port;
    @Override
    public String getName(Integer id) {
        HttpServletRequest request = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest();
        String name = request.getHeader("name");
        return StrUtil.format("nacos a({}) header:{} 返回的id: {}" ,this.port,name,id);
    }
    @Override
    public Integer getAmount(Integer id) {
        return id*100;
    }
    @Override
    public String getSleep(Integer time) {
        ThreadUtil.sleep(time*1000);
        return StrUtil.format("nacos a({}) 睡眠了 {} 秒",this.port ,time);
    }
}

打包运行a服务

 准备工作做完,直接运行访问

 在启动一个a服务

访问

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

沫洺

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

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

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

打赏作者

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

抵扣说明:

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

余额充值