## 前言

通过前面的学习,我们已经了解到如何使用 Feign 实现远程调用。然而,在使用过程中可能会遇到一个安全问题:微服务可能被任何人访问。为了解决这个问题,我们需要引入网关(Gateway)来对用户进行身份验证和权限校验。这样一来,所有请求都必须先经过网关,然后再转发到微服务,从而实现对微服务的保护。本文将介绍网关的功能、技术实现以及搭建过程。

### 1. 网关功能

#### 1.1 身份认证和权限校验

在用户通过身份认证后,网关会将请求转发到相应的微服务。这为微服务提供了一定程度的安全性。

#### 1.2 服务路由

网关根据请求信息判断应该将请求转发到哪个微服务,从而实现不同服务的区分和负载均衡。

#### 1.3 负载均衡

对外的服务也可能有多个实例,这时就需要网关进行负载均衡,以保证系统的稳定性和性能。

#### 1.4 请求限流

为了防止恶意用户对系统造成过大的压力,网关会对用户的请求量进行限制。

需要注意的是,Ribbon 是针对内部 userserver 做负载均衡的,而网关是针对外部 orderserver 做负载均衡的。

### 2. 网关的技术实现

目前常用的网关技术有以下两种:

#### 2.1 gataway

Gtatway 是一个新的技术,采用了响应式编程,非阻塞式,性能优越,吞吐能力强。但目前尚未得到广泛应用。

#### 2.2 zuul

Zuul 是较早的技术,基于 Servlet 实现,采用阻塞式编程,性能相对较差。尽管如此,它仍然具有一定的市场份额和应用场景。

### 3. 搭建网关服务

为了搭建一个网关服务,我们需要完成以下步骤:

1. 创建新的模块,并引入 SpringCloudGateway 依赖和 Nacos 服务发现依赖。

. 首先,在项目的pom.xml文件中添加nacos服务注册发现和网关gateway的依赖:

```xml

com.alibaba.cloud

spring-cloud-starter-alibaba-nacos-discovery

org.springframework.cloud

spring-cloud-starter-gateway

```

2. 接下来,创建一个启动类,例如`DemoApplication`,并添加`@EnableDiscoveryClient`注解以启用Nacos服务注册发现功能:

```java

import org.springframework.boot.SpringApplication;

import org.springframework.boot.autoconfigure.SpringBootApplication;

import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

@SpringBootApplication

@EnableDiscoveryClient

public class DemoApplication {

public static void main(String[] args) {

SpringApplication.run(DemoApplication.class, args);

}

}

```

3. 最后,编写路由配置及Nacos地址。路由配置由三部分组成:路由id、目标地址和路由断言(路由断言其实就是匹配规则)。路由断言用于判断请求是否符合要求,符合则转发到路由目的地。例如,以下是一个简单的路由配置示例:

```yaml

spring:

cloud:

gateway:

routes:

- id: user-service

uri: lb://user-service # 以负载均衡的方式访问名为user-service的服务实例

predicates:

- Path=/user/** # 当请求路径以/user/开头时,满足条件

```

网关配置

```yaml

server:

port: 10010 # 网关端口

spring:

application:

name: gateway # 服务名称

cloud:

nacos:

server-addr: localhost:8848 # Nacos地址

gateway:

routes: # 网关路由配置

- id: user-server # 路由id,自定义,只要唯一即可

uri: lb://userserver # 路由的目标地址,lb就是负载均衡,后面跟服务名

predicates: # 路由断言,也就是判断请求是否符合路由规则的条件

- Path=/user/** # 只要以/user/开头就符合要求

- id: order-server

uri: lb://orderserver

predicates:

- Path=/order/**

```

注意:路由不止一个,用 `-` 来表示路由数组!

您好,根据您提供的信息,您需要在Spring Cloud项目中使用网关。网关是微服务架构中起到入口和路由控制的关键组件。它负责处理客户端请求,进行路由决策,并将请求转发到相应的微服务。

您已经添加了spring-cloud-starter-gateway依赖,但是还需要排除掉spring-boot-starter-web和spring-boot-starter-webflux的依赖。具体操作如下:

```xml

org.springframework.cloud

spring-cloud-starter-gateway

org.springframework.boot

spring-boot-starter-web

org.springframework.boot

spring-boot-starter-webflux

```

在配置文件中,我们编写的断言规则仅仅是字符串。这些字符串会被断言工厂读取并处理,最终转变为路由判断的条件。Spring 提供了11种基本的Predicate工厂,其中Path是我们最常用的方式。

让我们来测试一下,将时间设定在2031年之后才能访问,现在肯定是不能访问的:只有当断言规则都满足的时候才能访问成功,路径符合,但时间不符合,照样不能访问。

接下来是路由过滤器部分。

4.1 普通过滤器

GatewayFilter是网关中提供的一种过滤器,可以处理进入网关的请求,以及微服务返回的响应。路由之后并不是立即向微服务发起请求的,实际上我们还可以给路由配置各种各样的过滤器,这些过滤器最终会形成一个过滤器链,对进入网关的请求做各种处理。

当请求给了微服务,微服务处理完之后会返回一个结果,这个结果也是先到达网关。网关同样会通过过滤器逐层处理这个响应结果,最终返回给用户。断言匹配成功相当于请求通过了网关的大门,然后过滤器会对请求做一些处理(比如添加请求头、请求参数等),最后网关会将处理好的请求代理到具体的服务,做业务处理。

Spring 提供了31种不同的路由过滤器工厂。我们可以在Controller中获取一下请求头,并在控制台输出。如果要对所有的路由都生效,则可以将过滤器工厂写到default-filters中,与routes处于同一级。

4.2 全局过滤器

全局过滤器即自定义过滤器,全局过滤器的作用也是处理一切进入网关的请求和微服务响应,与GatewayFilter的作用一样。区别在于GatewayFilter通过配置定义,处理逻辑是固定的;而GlobalFilter的逻辑需要自己写代码实现,定义方式是实现GlobalFilter接口。

下面是一个案例,定义全局过滤器拦截请求,判断请求的参数是否满足以下两个条件:1) 参数中是否有authorization;2) authorization的参数值是否为admin。如果同时满足则放行,否则拦截。

首先创建AuthorizeFilter类,实现GlobalFilter接口,并重写filter方法。第一个参数用于处理业务逻辑,第二个参数是用来放行的,把请求委托给下一个过滤器去处理,其实就是调用下一个过滤器的filter方法,等同于放行。

```java

public class AuthorizeFilter implements GlobalFilter {

@Override

public void doFilter(ServerWebExchange exchange, GatewayFilterChain chain) throws Exception {

// 获取请求头中的authorization字段

String authorization = exchange.getRequest().getHeaders().getFirst("Authorization");

// 如果authorization字段不存在或者不满足条件(例如值不是admin),则拦截请求

if (authorization == null || !authorization.equals("admin")) {

// 这里可以进行相应的错误处理或者重定向操作

return;

}

// 如果authorization字段存在且满足条件,则放行请求

chain.filter(exchange);

}

}

```

过滤器在处理请求时是有执行顺序的。这种顺序可以通过两种方式设置:通过注解,或者实现`Ordered`接口。数值越小,优先级越高。以下是这两种方法的具体实现:

1. 通过注解设置:

```java

@Component

public class AuthenticationFilter implements Filter {

@Override

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {

// 在这里可以进行一些过滤操作

chain.doFilter(request, response);

}

}

```

然后在配置类中为该过滤器添加一个`Order`注解,指定其执行顺序:

```java

@Configuration

public class WebConfig implements WebMvcConfigurer {

@Autowired

AuthenticationFilter authenticationFilter;

@Override

public void addInterceptors(InterceptorRegistry registry) {

registry.addInterceptor(authenticationFilter).addPathPatterns("/**").order(1); // Order值越小,优先级越高

}

}

```

2. 实现Ordered接口设置:

```java

@Component

public class MyFilter implements Filter, Ordered {

@Override

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {

// 在这里进行一些过滤操作

chain.doFilter(request, response);

}

@Override

public int getOrder() {

// 这里返回一个整数,数值越小,优先级越高;如果返回负数,表示最低优先级

return Integer.MAX_VALUE; // 例如这里我们将这个过滤器设置为最高优先级

}

}

```

同样地,在配置类中为该过滤器添加一个`Order`注解,以指定其执行顺序:

```java

@Configuration

public class WebConfig implements WebMvcConfigurer {

@Autowired

MyFilter myFilter;

@Override

public void addInterceptors(InterceptorRegistry registry) {

registry.addInterceptor(myFilter).addPathPatterns("/**").order(0); // Order值越小,优先级越高,0是最小的优先级,所以这里是最先执行的过滤器

}

}

```

以下是重构后的内容:

```java

package com.zxe.gateway;

import org.springframework.cloud.gateway.filter.GatewayFilterChain;

import org.springframework.cloud.gateway.filter.GlobalFilter;

import org.springframework.core.Ordered;

import org.springframework.core.annotation.Order;

import org.springframework.http.HttpStatus;

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;

// 定义过滤器执行的顺序,数值越小优先级越高

// 可以通过注解设置,也可以通过实现Ordered接口来设置

@Component

public class AuthorizeFilter implements GlobalFilter, Ordered {

@Override

public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {

//1.获取请求参数

ServerHttpRequest request = exchange.getRequest();

MultiValueMap params = request.getQueryParams();

//2.获取参数中的authorization参数

String auth = params.getFirst("authorization");

//3.判断参数是否等于admin

if ( "admin".equals(auth)) {

//4.等于admin放行,其实是调用下一个过滤器的filter方法,等同于放行

return chain.filter(exchange);

}

//5.不等于就拦截,后面的业务不会再继续,我们一般会设置状态码返回给用户,提示未登录

exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);

return exchange.getResponse().setComplete();

}

@Override

public int getOrder() {

return -1;

}

}

```

根据提供的内容,微服务网关过滤器执行顺序如下:

1. 用户请求进入网关,断言匹配失败,报 404 错误,匹配成功直接将请求送入过滤器链;

2. 过滤器分为普通过滤器和自定义过滤器,普通过滤器可以在配置文件中设置,自定义过滤器需要实现 GlobalFilter 接口,它可以处理更复杂的过滤业务;

3. 过滤不通过的请求就会被拦截,报401错误,只有层层过滤都通过了,请求才会被路由到具体的服务上,处理具体的业务。

当前路由过滤器和默认过滤器的执行顺序是由 Spring 指定的,默认按照声明顺序从 1 开始递增。当过滤器的 order 值一样时,会按照默认过滤器 > 当前路由过滤器 > 全局过滤器的顺序执行。

您好,您可以在Spring Cloud Gateway的配置文件中添加相关的跨域配置。 这包括指定允许访问的域,配置允许的HTTP方法,以及其他必要的头信息。

例如,您可以在`spring.cloud.gateway`下添加以下配置:

```

globalcors: #全局的跨域处理 add-to-simple-url-handler-mapping: true #解决options请求被拦截的问题 cors-configurations: '[/**]': allowedOrigins: #允许哪些网站的跨域请求 - "http://localhost:8090" - "http://www.leyou.com" allowedMethods: #允许的跨域ajax的请求方式 - "GET" - "POST" - "DELETE" - "PUT" - "OPTIONS" allowedHeaders: "*" #允许在请求中携带的头信息 allowCredentials: true #是否允许携带cookie maxAge: 360000 #这次跨域检测的有效期

```