SpringCloud系列教程:微服务的未来(十四)网关登录校验、自定义过滤器GlobalFilter、GatawayFilter

前言

在微服务架构中,API 网关扮演着至关重要的角色,负责路由请求、执行安全验证、流量控制等任务。Spring Cloud Gateway 作为一个强大的网关解决方案,提供了灵活的方式来实现这些功能。

本篇博客将重点介绍如何在 Spring Cloud Gateway 中实现网关登录校验,以及如何利用自定义过滤器(GlobalFilter 和 GatewayFilter)来处理请求和响应。通过这些技术,开发者可以在网关层统一处理认证、授权等功能,同时保持对路由的细粒度控制。


网关登录校验

登录是基于JWT实现的,JWT的校验算法较为复杂,并且需要使用秘钥。如果每个微服务都独立进行JWT校验,会面临两个主要问题:

  • 每个微服务都必须持有JWT的秘钥,这样会带来安全隐患。
  • 每个微服务需要重复编写登录校验和权限验证的代码,增加了开发和维护的复杂度。

网关是所有微服务的统一入口,所有请求都会先经过网关,可以将JWT的校验工作集中到网关中进行处理。这样做能够有效解决上述问题:

  • 秘钥只需在网关和用户服务中存储,避免了在每个微服务中重复存储秘钥的风险。
  • 登录校验功能只需要在网关中实现,简化了开发流程并避免了代码的重复编写。

    网络请求处理流程图:

在 Spring Cloud 微服务中,如果你想在网关转发请求之前进行登录校验(例如,验证JWT令牌),通常可以通过在网关中配置 全局过滤器(GlobalFilter) 来完成。Spring Cloud Gateway 提供了一种简便的方式来处理这一需求。

实现步骤:

  1. 创建一个全局过滤器

    Spring Cloud Gateway 提供了GlobalFilter接口,你可以通过实现该接口来定义网关请求的拦截和处理逻辑。

  2. 提取并验证 JWT

    在全局过滤器中,提取请求头中的Authorization字段中的JWT令牌,并使用签名密钥验证JWT是否合法。

  3. 决定是否继续转发请求

    根据JWT的有效性,如果令牌合法且未过期,则继续请求的转发。如果无效或过期,则直接返回401 Unauthorized响应。

自定义过滤器

自定义GlobalFilter

网关过滤器有两种,分别是:

  • GatewayFilter:路由过滤器,作用于任意指定的路由;默认不生效,要配置到路由后生效。
  • GlobalFilter:全局过滤器,作用范围是所有路由;声明后自动生效。

两种过滤器的方法签名一致:

复制代码
/**
 * 处理请求并将其传递给下一个过滤器
 * @param exchange 当前请求的上下文,其中包含request、response等各种数据
 * @param chain 过滤器链,基于它向下传递请求
 * @return 根据返回值标记当前请求是否被完成或拦截,chain.filter(exchange)就放行了。
 */
Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain);

创建MyGlobalFilter类

复制代码
package com.hmall.gateway.filters;

import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpHeaders;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

@Component
public class MyGlobalFilter implements GlobalFilter , Ordered {

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        //模拟登录逻辑
        ServerHttpRequest request = exchange.getRequest();
        HttpHeaders headers = request.getHeaders();
        System.out.println("headers = "+headers);
        //放行
        return chain.filter(exchange);

    }

    @Override
    public int getOrder() {
        //返回值越小,优先级越高
        return 0;
    }
}

在通常业务过程中GlobalFilter比自定义GatewayFilter更容易实现。

自定义过滤器GatewayFilter

自定义GatewayFilter不是直接实现GatewayFilter,而是实现AbstractGatewayFilterFactory,示例如下:

复制代码
@Component
public class PrintAnyGatewayFilterFactory extends AbstractGatewayFilterFactory<Object> {
    @Override
    public GatewayFilter apply(Object config) {
        return new GatewayFilter() {
            @Override
            public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
                // 编写过滤器逻辑
                System.out.println("PrintAny filter执行了");
                // 放行
                return chain.filter(exchange);
            }
        };
    }
}

对应的配置文件application.yaml如下:

复制代码
spring:
  cloud:
    gateway:
      default-filters:
            - AddRequestHeader=a,b # AddRequestHeader:这个过滤器会将指定的请求头添加到每个请求中。a,b 代表的是要添加的请求头和它的值,a 是请求头的名字,b 是请求头的值。
            - PrintAny=1,2,3 # 注意,这里多个参数以","隔开,将来会按照shortcutFieldOrder()方法返回的参数顺序依次复制

实现的PrintAnyGatewayFilterFactory类

复制代码
package com.hmall.gateway.filters;


import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

@Component
public class PrintAnyGatewayFilterFactory extends AbstractGatewayFilterFactory<Object> {


    @Override
    public GatewayFilter apply(Object config) {
        return new GatewayFilter() {

            @Override
            public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
                System.out.println("PrintAny filter执行成功");
                return chain.filter(exchange);
            }
        };
    }
}

如果需要设置对用的优先级,代码如下:

复制代码
	@Override
    public GatewayFilter apply(Object config) {
        return new OrderedGatewayFilter(new GatewayFilter() {

            @Override
            public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
                System.out.println("PrintAny filter执行成功");
                return chain.filter(exchange);
            }
        },1);
    }

当然,这种过滤器还支持配置参数,在PrintAnyGatewayFilterFactory类里面

复制代码
    // 自定义配置属性,成员变量名称很重要,下面会用到
    @Data
    static class Config{
        private String a;
        private String b;
        private String c;
    }
    // 将变量名称依次返回,顺序很重要,将来读取参数时需要按顺序获取
    @Override
    public List<String> shortcutFieldOrder() {
        return List.of("a", "b", "c");
    }
   	public PrintAnyGatewayFilterFactory(){
		super(Config.class);
	}
    // 返回当前配置类的类型,也就是内部的Config
    @Override
    public Class<Config> getConfigClass() {
        return Config.class;
    }

总结

  • Spring Cloud Gateway 是一个强大的网关工具,支持多种自定义过滤器。
  • GlobalFilter 是全局过滤器,可以应用于所有路由。
  • GatewayFilter 是路由级别的过滤器,可以对特定路由添加过滤逻辑。
  • 登录校验 可以通过 JWT 令牌等方式在网关中实现,以保证请求的安全性。

通过结合 GlobalFilter 和 GatewayFilter,你可以根据具体的需求为不同的路由应用不同的过滤逻辑,同时保证全局的一致性和可扩展性。

相关推荐
C182981825755 分钟前
restTemplate/Feign(Spring Cloud)或OKHttp Apache HttpClient 这几个关系与底层实现
spring cloud·okhttp·apache
代码or搬砖30 分钟前
Java集合-Set讲解
java·开发语言
渣娃-小晴晴31 分钟前
java集合在并发环境下应用时的注意事项
java·后端
北极糊的狐32 分钟前
若依系统报错net::ERR_CONNECTION_TIMED_OUT的原因
java·windows·sql·mybatis
FLGB1 小时前
maven漏洞检测报告
java·maven
农夫山泉2号2 小时前
【c++】——c++编译的so中函数有额外的字符
java·服务器·c++
wangan0942 小时前
不带圆圈的二叉树
java·前端·javascript
小马哥编程2 小时前
【软考架构】滑动窗口限流算法的原理是什么?
java·开发语言·架构
饕餮争锋2 小时前
Spring AOP原理简析
java·spring
okseekw2 小时前
Maven从入门到实战:核心概念+配置详解+避坑指南
java·后端