程序员scholar 程序员scholar
首页
  • Java 基础

    • JavaSE
    • JavaIO
    • JavaAPI速查
  • Java 高级

    • JUC
    • JVM
    • Java新特性
    • 设计模式
  • Web 开发

    • Servlet
    • Java网络编程
  • Web 标准

    • HTML
    • CSS
    • JavaScript
  • 前端框架

    • Vue2
    • Vue3
    • Vue3 + TS
    • 微信小程序
    • uni-app
  • 工具与库

    • jQuery
    • Ajax
    • Axios
    • Webpack
    • Vuex
    • WebSocket
    • 第三方登录
  • 后端与语言扩展

    • ES6
    • Typescript
    • node.js
  • Element-UI
  • Apache ECharts
  • 数据结构
  • HTTP协议
  • HTTPS协议
  • 计算机网络
  • Linux常用命令
  • Windows常用命令
  • SQL数据库

    • MySQL
    • MySQL速查
  • NoSQL数据库

    • Redis
    • ElasticSearch
  • 数据库

    • MyBatis
    • MyBatis-Plus
  • 消息中间件

    • RabbitMQ
  • 服务器

    • Nginx
  • Spring框架

    • Spring6
    • SpringMVC
    • SpringBoot
    • SpringSecurity
  • SpringCould微服务

    • SpringCloud基础
    • 微服务之DDD架构思想
  • 日常必备

    • 开发常用工具包
    • Hutoll工具包
    • IDEA常用配置
    • 开发笔记
    • 日常记录
    • 项目部署
    • 网站导航
    • 产品学习
    • 英语学习
  • 代码管理

    • Maven
    • Git教程
    • Git小乌龟教程
  • 运维工具

    • Docker
    • Jenkins
    • Kubernetes
  • 算法笔记

    • 算法思想
    • 刷题笔记
  • 面试问题常见

    • 十大经典排序算法
    • 面试常见问题集锦
关于
GitHub (opens new window)
首页
  • Java 基础

    • JavaSE
    • JavaIO
    • JavaAPI速查
  • Java 高级

    • JUC
    • JVM
    • Java新特性
    • 设计模式
  • Web 开发

    • Servlet
    • Java网络编程
  • Web 标准

    • HTML
    • CSS
    • JavaScript
  • 前端框架

    • Vue2
    • Vue3
    • Vue3 + TS
    • 微信小程序
    • uni-app
  • 工具与库

    • jQuery
    • Ajax
    • Axios
    • Webpack
    • Vuex
    • WebSocket
    • 第三方登录
  • 后端与语言扩展

    • ES6
    • Typescript
    • node.js
  • Element-UI
  • Apache ECharts
  • 数据结构
  • HTTP协议
  • HTTPS协议
  • 计算机网络
  • Linux常用命令
  • Windows常用命令
  • SQL数据库

    • MySQL
    • MySQL速查
  • NoSQL数据库

    • Redis
    • ElasticSearch
  • 数据库

    • MyBatis
    • MyBatis-Plus
  • 消息中间件

    • RabbitMQ
  • 服务器

    • Nginx
  • Spring框架

    • Spring6
    • SpringMVC
    • SpringBoot
    • SpringSecurity
  • SpringCould微服务

    • SpringCloud基础
    • 微服务之DDD架构思想
  • 日常必备

    • 开发常用工具包
    • Hutoll工具包
    • IDEA常用配置
    • 开发笔记
    • 日常记录
    • 项目部署
    • 网站导航
    • 产品学习
    • 英语学习
  • 代码管理

    • Maven
    • Git教程
    • Git小乌龟教程
  • 运维工具

    • Docker
    • Jenkins
    • Kubernetes
  • 算法笔记

    • 算法思想
    • 刷题笔记
  • 面试问题常见

    • 十大经典排序算法
    • 面试常见问题集锦
关于
GitHub (opens new window)
npm

(进入注册为作者充电)

  • 微服务基础

    • 了解SpringCloud微服务架构
    • 启动多个微服务项目
    • Eureka 服务注册
    • Ribbon 负载均衡
    • Nacos 下载与安装
    • Nacos 服务注册
    • Nacos 配置管理
    • openFeign 远程调用
    • OpenFeign 最佳实践
    • Gateway 网关
    • Gateway 进阶使用
      • 1. 断言工厂 (Predicate Factory)
      • 2. 过滤器工厂 (Filter Factory)
        • 1. 常见的过滤器工厂
        • 2. 请求头过滤器示例
        • 3. 全局默认过滤器
      • 3. Gateway 全局过滤器
        • 1. 全局过滤器的作用
        • 2. 自定义全局过滤器示例
        • 2.1 自定义全局过滤器
        • 2.2 测试过滤器效果
        • 3. 过滤器执行顺序
        • 3.1 通过实现 Ordered 接口设置顺序
        • 3.2 通过 @Order 注解设置顺序
        • 4. 全局过滤器小结
      • 4. Gateway 跨域问题详解
        • 1. 什么是跨域请求
        • 2. 跨域请求的完整流程
        • 3. 同源策略
        • 4. SpringBoot 中的跨域解决方案
        • 4.1 使用 Spring 提供的跨域配置
        • 4.2 在微服务架构中通过 Gateway 配置跨域
        • 4.3 通过 Nginx 反向代理解决跨域问题
        • 5. 跨域问题总结
    • Sentinel 服务保护
    • Sentinel 整合 Feign
    • 分布式事务 Seata
    • 什么是分布式
    • 什么是RPC框架
  • 微服务之DDD架构思想

  • 微服务
  • 微服务基础
scholar
2024-08-24
目录

Gateway 进阶使用

# Gateway 进阶

在 SpringCloud Gateway 中,断言工厂 和 过滤器工厂 是实现网关功能的核心组件,它们帮助我们对请求进行条件匹配和处理,最终实现灵活的路由配置和请求管理。

# 1. 断言工厂 (Predicate Factory)

断言工厂用于根据请求的属性判断该请求是否符合路由规则。例如,路径匹配规则 Path=/user/** 由 org.springframework.cloud.gateway.handler.predicate.PathRoutePredicateFactory 处理。类似的断言工厂在 SpringCloud Gateway 中有十几种。

常见的断言工厂包括:

名称 说明 示例
After 请求发生在某个时间点之后 After=2037-01-20T17:42:47.789-07:00[America/Denver]
Before 请求发生在某个时间点之前 Before=2031-04-13T15:14:47.433+08:00[Asia/Shanghai]
Between 请求发生在两个时间点之间 Between=2037-01-20T17:42:47.789-07:00,2037-01-21T17:42:47.789-07:00
Cookie 请求必须包含指定的 Cookie Cookie=chocolate,ch.p
Header 请求必须包含指定的 Header Header=X-Request-Id,\d+
Host 请求必须访问特定的主机 (域名) Host=**.somehost.org,**.anotherhost.org
Method 请求方法必须为指定的方式 Method=GET,POST
Path 请求路径必须符合指定规则 Path=/red/{segment},/blue/**
Query 请求参数必须包含指定的参数 Query=name,Jack
RemoteAddr 请求的 IP 必须在指定范围内 RemoteAddr=192.168.1.1/24
Weight 权重处理 配置权重路由的负载分配

说明:我们只需要掌握Path这种路由工程就可以了。

  • Path=/user/** 是基于路径的断言,匹配所有以 /user/ 开头的请求。
  • 断言工厂在 Gateway 中起到条件判断的作用,可以通过配置实现灵活的路由规则。

# 2. 过滤器工厂 (Filter Factory)

过滤器工厂在网关中用于处理请求和响应,可以添加或移除请求头、修改请求路径、限流等操作。SpringCloud Gateway 提供了丰富的内置过滤器工厂,常见的有请求头过滤、限流过滤、路径修改过滤等。

内置的过滤器工厂可以根据用途分为以下几类:

  • Header 相关:如 AddRequestHeader、RemoveRequestHeader 等。
  • Parameter 相关:如 AddRequestParameter、RemoveRequestParameter 等。
  • Path 相关:如 RewritePath、StripPrefix 等。
  • Body 相关:如 ModifyRequestBody、ModifyResponseBody 等。
  • Status 相关:如 SetStatus、SetResponseStatus 等。
  • Session 相关:如 SaveSession、RedisRateLimiter 等。
  • Redirect 相关:如 RedirectTo 等。
  • Retry 相关:如 Retry 过滤器。
  • RateLimiter:如 RequestRateLimiter 限流过滤器。
  • Hystrix:如 Hystrix 过滤器,用于熔断处理。

过滤器工厂示例

GatewayFilter是网关中提供的一种过滤器,可以对进入网关的请求和微服务返回的响应做处理:

image-20230917225932875

# 1. 常见的过滤器工厂

SpringCloud Gateway 提供了 37 种不同的路由过滤器工厂。例如:

名称 说明
AddRequestHeader 给当前请求添加一个请求头
RemoveRequestHeader 移除请求中的一个请求头
AddResponseHeader 给响应结果中添加一个响应头
RemoveResponseHeader 从响应结果中移除某个响应头
RequestRateLimiter 限制请求流量

这些过滤器工厂可以帮助我们灵活地管理和处理网关中的请求。

过滤器工厂示例

# 2. 请求头过滤器示例

下面以 AddRequestHeader 为例,演示如何为所有进入 userservice 的请求添加一个自定义请求头 Truth=给请求头加过滤器测试!。

在 gateway 服务的 application.yml 文件中配置路由过滤器:

spring:
  cloud:
    gateway:
      routes: # 网关路由配置
        - id: userservice # 路由id,自定义,只要唯一即可
          uri: lb://userservice # 路由的目标地址 lb就是负载均衡,后面跟服务名称
          predicates: # 路由断言,也就是判断请求是否符合路由规则的条件
            - Path=/user/** # 这个是按照路径匹配,只要以/user/开头就符合要求
          filters: # 过滤器
            - AddRequestHeader=Truth, 给请求头加过滤器测试! # 添加请求头
        - id: orderservice # 路由id,自定义,只要唯一即可
          uri: lb://orderservice # 路由的目标地址 lb就是负载均衡,后面跟服务名称
          predicates: # 路由断言,也就是判断请求是否符合路由规则的条件
            - Path=/order/** # 这个是按照路径匹配,只要以/order/开头就符合要求
1
2
3
4
5
6
7
8
9
10
11
12
13
14

重点说明:

  • 当前 AddRequestHeader 过滤器配置在 userservice 路由下,因此仅对访问 userservice 的请求生效,其他路由(如 orderservice)不会受此影响。

# 3. 全局默认过滤器

如果希望对所有路由生效,可以使用全局默认过滤器:

spring:
  cloud:
    gateway:
      routes:
        - id: userservice 
          uri: lb://userservice 
          predicates: 
            - Path=/user/**
      default-filters: # 默认过滤项
        - AddRequestHeader=Truth, 给请求头加过滤器测试! # 添加请求头
1
2
3
4
5
6
7
8
9
10

说明:

  • 全局默认过滤器会对所有经过网关的请求生效,无论路由规则如何。

小结

  1. 断言工厂:用于判断请求是否符合路由规则。常见的断言包括 Path、Method、Header 等,可以根据请求的路径、方法、头信息等进行路由匹配。
  2. 过滤器工厂:用于对请求或响应进行处理,如添加请求头、限流、修改路径等。过滤器可以配置在特定路由下,也可以作为全局过滤器,提供灵活的请求管理能力。
  3. 实用场景:通过合理配置断言和过滤器,可以实现复杂的网关逻辑,包括限流、鉴权、负载均衡等功能,使得微服务架构中的流量管理更加高效和灵活。

# 3. Gateway 全局过滤器

在使用 SpringCloud Gateway 时,内置的过滤器虽然功能丰富,但逻辑是固定的。如果我们需要在请求进入网关时执行自定义逻辑,如登录校验、权限验证等操作,就需要使用全局过滤器。

# 1. 全局过滤器的作用

全局过滤器与 GatewayFilter 的作用相似,都是用于处理进入网关的请求和微服务的响应。区别在于:

  • GatewayFilter 是通过配置实现,处理逻辑固定。
  • GlobalFilter 是通过代码实现,可以自定义处理逻辑。

全局过滤器的定义需要实现 GlobalFilter 接口:

public interface GlobalFilter {
    /**
     * 处理当前请求,可以通过 {@link GatewayFilterChain} 将请求交给下一个过滤器处理
     *
     * @param exchange 请求上下文,包含 Request、Response 等信息
     * @param chain    用来把请求委托给下一个过滤器
     * @return {@code Mono<Void>} 表示当前过滤器处理完毕
     */
    Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain);
}
1
2
3
4
5
6
7
8
9
10

在 filter 方法中编写自定义逻辑,可以实现以下功能:

  • 登录状态判断
  • 权限校验
  • 请求限流等

# 2. 自定义全局过滤器示例

需求:定义一个全局过滤器,拦截请求并判断请求的参数是否满足以下条件:

  • 请求参数中是否包含 authorization。
  • authorization 参数的值是否为 admin。

如果以上条件同时满足,则放行请求;否则拦截请求。

# 2.1 自定义全局过滤器

在 gateway 服务中创建自定义过滤器 AuthorizeFilter:

package cn.itcast.gateway.filters;

import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.util.MultiValueMap;
import reactor.core.publisher.Mono;

@Order(-1) // 指定过滤器的顺序,order 值越小,优先级越高
@Component // 将过滤器注册为 Spring 容器中的组件
public class AuthorizeFilter implements GlobalFilter {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // 1. 获取请求参数
        MultiValueMap<String, String> params = exchange.getRequest().getQueryParams();
        // 2. 获取 authorization 参数
        String auth = params.getFirst("authorization");
        // 3. 校验 authorization 参数
        if (auth == null || "".equals(auth)) {
            // 如果没有提供 authorization 参数,返回 401 Unauthorized 状态码
            exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
            return exchange.getResponse().setComplete(); // 拦截请求
        }
        if ("admin".equals(auth)) {
            // 如果 authorization 参数为 "admin",放行请求
            return chain.filter(exchange);
        }
        // 如果 authorization 参数不为 "admin",返回 403 Forbidden 状态码
        exchange.getResponse().setStatusCode(HttpStatus.FORBIDDEN);
        return exchange.getResponse().setComplete(); // 拦截请求
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

重点说明:

  • ServerWebExchange 提供了请求和响应的上下文信息。
  • 通过 order 属性或实现 Ordered 接口来控制过滤器的执行顺序。

# 2.2 测试过滤器效果

重启网关服务,访问 http://localhost:10010/user/1,由于没有提供 authorization 参数,因此请求被拦截:

未授权请求被拦截

添加 authorization=admin 参数后,访问 http://localhost:10010/user/1?authorization=admin,请求被放行并成功返回结果:

授权请求成功

# 3. 过滤器执行顺序

请求进入网关时,会经过三类过滤器:

  • 当前路由的 过滤器 (如配置在 application.yml 中的路由过滤器)。
  • DefaultFilter:全局默认过滤器。
  • GlobalFilter:自定义的全局过滤器。

过滤器执行顺序:

  1. 默认过滤器 (DefaultFilter)
  2. 路由过滤器 (配置在特定路由下的过滤器)
  3. 全局过滤器 (GlobalFilter)

image-20240823235228873

过滤器的执行顺序由 order 值决定,order 值越小,优先级越高,执行顺序越靠前。

# 3.1 通过实现 Ordered 接口设置顺序

@Component
public class AuthorizeFilter implements GlobalFilter, Ordered {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // 过滤逻辑同前
    }

    @Override
    public int getOrder() {
        return -1; // order 值越小,优先级越高
    }
}
1
2
3
4
5
6
7
8
9
10
11
12

# 3.2 通过 @Order 注解设置顺序

@Component
@Order(-1) // 指定过滤器的顺序
public class AuthorizeFilter implements GlobalFilter {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // 过滤逻辑同前
    }
}
1
2
3
4
5
6
7
8

注意:路由过滤器和 DefaultFilter 的 order 值由 Spring 自动分配,通常按照声明顺序从 1 开始递增。当多个过滤器的 order 值相同时,执行顺序为:

  1. DefaultFilter
  2. 路由过滤器
  3. GlobalFilter

详细内容,可以查看执行顺序的源码:

org.springframework.cloud.gateway.route.RouteDefinitionRouteLocator#getFilters()方法是先加载defaultFilters,然后再加载某个route的filters,然后合并。

org.springframework.cloud.gateway.handler.FilteringWebHandler#handle()方法会加载全局过滤器,与前面的过滤器合并后根据order排序,组织过滤器链

# 4. 全局过滤器小结

  1. 全局过滤器:实现自定义逻辑的过滤器,用于处理所有进入网关的请求和响应。
  2. 过滤器执行顺序:由 order 值控制,值越小,优先级越高。
  3. 测试方法:通过指定请求参数控制过滤器的放行和拦截逻辑,实现自定义的业务规则。

# 4. Gateway 跨域问题详解

# 1. 什么是跨域请求

跨域请求是指当前发起请求的域与请求目标资源所在的域不同时的请求。跨域的判断标准包括协议、域名、端口号三者中的任何一个不同。

常见的跨域问题场景如下:

  • 前端页面从 http://localhost:8080 请求后端 API http://localhost:8081。
  • 不同域名、子域名或端口之间的资源请求。

当跨域请求发生时,如果服务器未正确处理跨域问题,浏览器会基于同源策略(Same-Origin Policy)拒绝请求。

# 2. 跨域请求的完整流程

跨域请求通常涉及两个请求过程:

  1. 预检请求(OPTIONS 请求):在发送实际请求前,浏览器会首先发起一个 OPTIONS 请求,用于验证服务器是否允许跨域请求。如果服务器允许跨域请求,则浏览器会继续发送实际请求。
  2. 实际请求:在预检请求成功后,浏览器会发送实际的跨域请求,服务器响应请求并返回数据。

注意:如果服务器配置了允许所有源的请求(使用通配符 *),或者源是相同的,同源策略不适用,那么可能不会触发预检请求。但这通常不是最佳安全实践。

# 3. 同源策略

同源策略(Same-Origin Policy)是一种浏览器安全机制,它限制了脚本在不同源之间自由交互数据。其主要目的是防止恶意网站获取用户敏感信息或执行恶意操作。

同源指的是协议、域名和端口号完全相同的两个 URL。如果这三者中任何一个不同,就会触发跨域问题。

# 4. SpringBoot 中的跨域解决方案

# 4.1 使用 Spring 提供的跨域配置

Spring 提供了简化跨域配置的机制,可以通过注解或全局配置来解决跨域问题。

  1. 使用 @CrossOrigin 注解:可以在 Controller 层为特定方法或整个类配置跨域规则。

  2. 全局跨域配置:使用 WebMvcConfigurer 配置全局跨域规则:

    @Configuration
    public class WebConfig implements WebMvcConfigurer {
    
        @Override
        public void addCorsMappings(CorsRegistry registry) {
            registry.addMapping("/**") // 设置路径匹配模式
                    .allowedOrigins("http://localhost:9000") // 允许的跨域来源
                    .allowedMethods("GET", "POST", "PUT", "DELETE") // 允许的 HTTP 方法
                    .allowedHeaders("Origin", "Content-Type", "Accept") // 允许的 HTTP 头部
                    .allowCredentials(true) // 是否允许携带凭证
                    .maxAge(3600); // 缓存时间
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13

说明:上述配置允许 http://localhost:9000 进行跨域请求,并且支持特定的 HTTP 方法和头部。

# 4.2 在微服务架构中通过 Gateway 配置跨域

在 SpringCloud Gateway 项目中,可以在 application.yml 中配置全局跨域处理:

spring:
  cloud:
    gateway:
      globalcors: # 全局跨域配置
        add-to-simple-url-handler-mapping: true # 处理 OPTIONS 请求
        corsConfigurations:
          '[/**]': # 对所有路径生效
            allowedOrigins: # 允许的跨域来源
              - "http://localhost:9000"
            allowedMethods: # 允许的请求方法
              - "GET"
              - "POST"
              - "DELETE"
              - "PUT"
              - "OPTIONS"
            allowedHeaders: "*" # 允许的请求头
            allowCredentials: true # 是否允许携带凭证
            maxAge: 360000 # 预检请求的有效期
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

重点说明:

  • globalcors 配置项用于全局跨域处理,add-to-simple-url-handler-mapping 解决 OPTIONS 请求被拦截的问题。
  • allowedOrigins 和 allowedMethods 分别配置允许的跨域来源和请求方法。

# 4.3 通过 Nginx 反向代理解决跨域问题

在使用 Nginx 时,可以在配置文件中添加跨域处理:

server {
    listen 80;
    server_name localhost;

    location / {
        proxy_pass http://localhost:8081;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;

        # 添加跨域头部信息
        add_header 'Access-Control-Allow-Origin' 'http://localhost:8080';
        add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
        add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
        
        # 处理 OPTIONS 请求
        if ($request_method = 'OPTIONS') {
            add_header 'Access-Control-Allow-Credentials' 'true';
            add_header 'Access-Control-Max-Age' 1728000;
            add_header 'Content-Type' 'text/plain charset=UTF-8';
            add_header 'Content-Length' 0;
            return 204;
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

重点说明:

  • proxy_pass 配置了反向代理的目标地址。
  • add_header 添加了跨域头部,允许指定来源的请求。

# 5. 跨域问题总结

  • 跨域问题的必要性和重要性:跨域问题的处理是确保安全性、用户隐私和系统稳定性的关键。
  • 选择合适的跨域解决方案:根据项目需求和部署环境,选择合适的跨域解决方案,例如注解配置、全局配置、网关配置或 Nginx 配置。
  • 分布式系统中的跨域问题:在微服务架构中,跨域问题尤为常见,特别是在多个服务之间进行通信时,正确的跨域配置至关重要。

通过对跨域的理解和解决方案的掌握,可以确保系统在复杂环境中依然保持稳定、安全和高效的运行。

编辑此页 (opens new window)
上次更新: 2024/12/28, 18:32:08
Gateway 网关
Sentinel 服务保护

← Gateway 网关 Sentinel 服务保护→

Theme by Vdoing | Copyright © 2019-2025 程序员scholar
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式