一、网关基本概念

1.1、API网关介绍

API 网关出现的原因是微服务架构的出现,不同的微服务一般会有不同的网络地址,而外部客户端可能需要调用多个服务的接口才能完成一个业务需求,如果让客户端直接与各个微服务通信,会有以下的问题:

  • 客户端会多次请求不同的微服务,增加了客户端的复杂性。

  • 存在跨域请求,在一定场景下处理相对复杂。

  • 认证复杂,每个服务都需要独立认证。

  • 难以重构,随着项目的迭代,可能需要重新划分微服务。例如,可能将多个服务合并成一个或者将一个服务拆分成多个。如果客户端直接与微服务通信,那么重构将会很难实施。

以上这些问题可以借助 API 网关解决。API 网关是介于客户端和服务器端之间的中间层,所有的外部请求都会先经过 API 网关这一层。也就是说,API 的实现方面更多的考虑业务逻辑,而安全、性能、监控可以交由 API 网关来做,这样既提高业务灵活性又不缺安全性

假设一个电商项目,购买一件商品需要调用多个微服务接口,才能完成下单操作,在没有使用网关之前,用户的操作流程图是这样的:

image-20210211121505821

在引入网关后,架构可以演变为下图

image-20210211121759523

微服务网关是程序的大门,封装了应用程序的内部结构,用户只需要跟网关交互,无需调用特定微服务接口。

这样,开发就可以得到简化。不仅如此,使用微服务网关还有以下优点:

  • 易于监控。可在微服务网关收集监控数据并将其推送到外部系统进行分析。
  • 易于认证。可在微服务网关上进行认证,然后再将请求转发到后端的微服务,而无须在每个微服务中进行认证。
  • 减少了客户端与各个微服务之间的交互次数。

1.2、Spring Cloud GateWay

1、概述

Spring cloud gateway是spring官方基于Spring 5.0、Spring Boot2.0和Project Reactor等技术开发的网关,Spring Cloud Gateway旨在为微服务架构提供简单、有效和统一的API路由管理方式,Spring Cloud Gateway作为Spring Cloud生态系统中的网关,目标是替代Netflix Zuul,其不仅提供统一的路由方式,并且还基于Filer链的方式提供了网关基本的功能,例如:安全、监控/埋点、限流等。

Spring Cloud Gateway作为Spring Cloud生态系统中的网关,目的是为了替代Zuul,在Spring Cloud2.0以上版本中,没有对新版本的Zuul 2.0以上的新高版本进行集成,仍然还是使用Zuul 1.x非Reactor模式的老版本。Gateway是基于webFlux框架实现的WebFlux框架底层使用了高性能的Reactor模式通信框架Netty

img

作用:

  • 反向代理
  • 统一鉴权
  • 流量控制
  • 熔断
  • 日志监控

所处位置

img

2、核心概念

  • 路由:路由是网关最基础的部分,路由信息有一个ID、一个目的URL、一组断言和一组Filter组成。如果断言路由为真,则说明请求的URL和配置匹配

    • id:路由id,没有起名规则,但要求唯一
    • uri:匹配路由的转发地址
    • predicates:配置该路由的断言,若符合该断言则会转发到匹配的地址
    • order:路由的优先级,数字越小优先级越高
  • 断言:Java8中的断言函数。Spring Cloud Gateway中的断言函数允许开发者去定义匹配来自于http request中的任何信息,比如请求头和参数等。

  • 过滤器:一个标准的Spring webFilter。Spring cloud gateway中的filter分为两种类型的Filter,分别是Gateway Filter和Global Filter。过滤器Filter将会对请求和响应进行修改处理。

3、执行流程

如下图所示,Spring cloud Gateway发出请求。然后再由Gateway Handler Mapping中找到与请求相匹配的路由,将其发送到Gateway web handler。Handler再通过指定的过滤器链将请求发送到我们实际的服务执行业务逻辑,然后返回。

image-20210211130417182

  • Filter中,”pre”类型的过滤器可以做参数校验、权限校验、流量控制、日志输出和协议转换等。”post”类型的过滤器可以做相应内容、响应头的修改、日志的输出、流量监控。
  • 核心逻辑:路由转发+执行过滤器链。

4、特点

优点:

  • 性能强劲:是第一代网关Zuul的1.6倍
  • 功能强大:内置了很多实用的功能,例如转发、监控、限流等
  • 设计优雅,容易扩展

缺点:

  • 其实现依赖Netty与WebFlux,不是传统的Servlet编程模型,学习成本高
  • 不能将其部署在Tomcat、Jetty等Servlet容器里,只能打成jar包执行
  • 需要Spring Boot 2.0及以上的版本,才支持

二、网关使用

2.1、创建网关微服务cloud-gateway-9527

pom.xml

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
36
37
<dependencies>
<!--新增gateway-->

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.cloudstudy</groupId>
<artifactId>common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>

application.yml配置文件

application.yml文件中添加路由配置

  • -:表示数组元素,可以配置多个节点
  • id:配置的唯一标识,可以和微服务同名,也可以起别的名字,区别于其他 Route。
  • uri:路由指向的目的地 uri,即客户端请求最终被转发到的微服务。
  • predicates:断言的作用是进行条件判断,只有断言都返回真,才会真正的执行路由。
  • Path:路径形式的断言。当匹配这个路径时,断言条件成立
  • /**:一个或多个层次的路径
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
server:
port: 9527
spring:
application:
name: cloud-gateway #注册到eureka中的应用名
cloud:
gateway:
routes:
- id: payment_routh #路由id,没有命名规则但要求唯一,建议配合服务名
uri: http://localhost:8001
predicates:
- Path=/payment/get/** #断言,路径相匹配的进行路由
- id: payment_routh2
uri: http://localhost:8001
predicates:
- Path=/payment/lb/** #断言,路径相匹配的进行路由
eureka:
instance:
hostname: cloud-gateway-service
client:
service-url:
register-with-eureka: true
fetch-registry: true
defaultZone: http://eureka7001.com:7001/eureka

主启动类

1
2
3
4
5
6
7
@EnableEurekaClient
@SpringBootApplication
public class GatewayMain9527 {
public static void main(String[] args) {
SpringApplication.run(GatewayMain9527.class);
}
}

2.2、Payment8001

在payment微服务中有两个重要接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@GetMapping("/get/{id}")
@ApiOperation("根据id查询订单")
public CommonResult<Payment> getPaymentById(@PathVariable("id") Long id) {
Payment result = paymentService.getPaymentById(id);
if(result == null) {
return new CommonResult<>(404,"数据库中没有该订单!端口为:" + serverPort,null);
}
return new CommonResult<>(200,"成功!端口为:" + serverPort,result);
}

@GetMapping("/lb")
public String getPaymentLB() {
return serverPort;
}

我们希望通过网关在payment服务的8001端口外再包裹一层9527端口,让用户无法直接访问8001端口。

2.3、启动微服务,进行测试

启动eureka7001、payment8001和gateway9527

image-20210211132551232

通过localhost:8001/payment/lb访问payment微服务

image-20210211132713170

通过网关,即9527端口访问payment微服务

image-20210211133144911

访问说明

  • payment微服务控制器的访问路径

image-20210211133942958

  • 如果访问路径匹配路由断言,则转发到指定uri

image-20210211134259898

2.4、配置路由的另一种方式

使用网关,让网关帮我们跳转到百度新闻页面

1、创建一个GateWayConfig配置类

1
2
3
4
5
6
7
8
9
10
11
12
13
@Configuration
public class GatewayConfig {
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder routeLocatorBuilder) {
RouteLocatorBuilder.Builder routes = routeLocatorBuilder.routes();
routes.route("path_route_wuhu",
r -> r.path("/guonei").uri("http://news.baidu.com/guonei"));
routes.route("path_route_qifei",
r -> r.path("/guoji").uri("http://news.baidu.com/guoji"));

return routes.build();
}
}

2、测试

访问http://localhost:9527/guoji

image-20210211135215779

同理访问http://localhost:9527/guonei

image-20210211135303881

2.5、配合注册中心实现动态路由

默认情况下Gateway会根据注册中心的服务列表,以注册中心上微服务名为路径创建动态路由进行转发,从而实现动态路由的功能

1、启动

根据payment8001微服务复刻出另一个类似的payment微服务,端口号为8002

启动eureka7001+payment8001+payment8002

2、修改网关微服务配置文件

在路由的uri中使用lb://微服务名称的方法来动态配置路由,并实现负载均衡,使用步骤如下

  • 在配置文件中开启从注册中心动态创建路由的功能,使用微服务名进行路由
1
2
3
4
5
6
7
spring:
cloud:
gateway:
# 开启从注册中心动态创建路由的功能,使用微服务名进行路由
discovery:
locator:
enabled: true
  • 在配置文件中将路由uri修改为lb://微服务名来指定跳转地址,并实现负载均衡,需要注意的是uri的协议为lb,表示启用Gateway的负载均衡功能。
1
2
3
4
5
6
7
8
9
10
11
routes:
- id: payment_routh #路由id,没有命名规则但要求唯一,建议配合服务名
# 使用注册中心中注册的微服务名实现动态路由
uri: lb://cloud-payment-service
predicates:
- Path=/payment/get/** #断言,路径相匹配的进行路由
- id: payment_routh2
uri: lb://cloud-payment-service
predicates:
- Path=/payment/lb/** #断言,路径相匹配的进行路由

  • 网关微服务完整配置文件如下
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
server:
port: 9527

spring:
application:
name: cloud-gateway #注册到eureka中的应用名
cloud:
gateway:
# 开启从注册中心动态创建路由的功能,使用微服务名进行路由
discovery:
locator:
enabled: true
routes:
- id: payment_routh #路由id,没有命名规则但要求唯一,建议配合服务名
# 使用注册中心中注册的微服务名实现动态路由
uri: lb://cloud-payment-service
predicates:
- Path=/payment/get/** #断言,路径相匹配的进行路由
- id: payment_routh2
uri: lb://cloud-payment-service
predicates:
- Path=/payment/lb/** #断言,路径相匹配的进行路由

eureka:
instance:
hostname: cloud-gateway-service
client:
service-url:
register-with-eureka: true
fetch-registry: true
defaultZone: http://eureka7001.com:7001/eureka

3、测试

启动网关微服务,查看eureka注册中心情况

image-20210211144518120

测试,访问payment微服务的lb方法,查看调用端口情况

  • 第一次

image-20210211144610176

  • 第二次

image-20210211144625837

这样就实现了动态路由和负载均衡功能。

2.6、Predicate的使用

Spring Cloud Gateway内置断言工厂,用于进行条件判断,只有断言都返回真,才会真正执行路由。

内置的断言工厂具体如下

1、基于DateTime

此类型的断言根据时间做判断,主要有三个:

  • AfterRoutePredicateFactory: 接收一个日期参数,判断请求日期是否晚于指定日期
  • BeforeRoutePredicateFactory: 接收一个日期参数,判断请求日期是否早于指定日期
  • BetweenRoutePredicateFactory: 接收两个日期参数,判断请求日期是否在指定时间段内

这里的时间格式可以由JDK8自带的ZoneDateTime生成

1
2
3
4
public static void main(String[] args) {
ZonedDateTime dateTime = ZonedDateTime.now();
System.out.println(dateTime);
}
  • 结果

image-20210211153541725

AfterRoutePredicateFactory

  • 在yml配置文件中添加配置如下,这里将after后面的时间设置为我当前时间的一个笑是后
1
2
3
4
5
- id: payment_routh2
uri: lb://cloud-payment-service
predicates:
- Path=/payment/lb/** #断言,路径相匹配的进行路由
- After=2021-02-11T16:37:54.926+08:00[Asia/Shanghai]

image-20210211153734924

  • 结果

image-20210211153920543

  • 修改yml配置文件,将时间改为当前时间一个小时前,重启微服务
1
- After=2021-02-11T14:37:54.926+08:00[Asia/Shanghai]
  • 结果

image-20210211154031133

同理,Before断言和Between断言与After的使用方式类型,只是Between断言需要传入两个时间,如

1
- Between=2020-03-08T10:59:34.102+08:00[Asia/Shanghai] ,  2020-03-08T10:59:34.102+08:00[Asia/Shanghai]

2、基于远程地址

RemoteAddrRoutePredicateFactory:接收一个IP地址段,判断请求主机地址是否在地址段中

1
- RemoteAddr=192.168.1.1/24

修改yml配置文件,进行测试

1
2
3
4
5
6
- id: payment_routh2
uri: lb://cloud-payment-service
predicates:
- Path=/payment/lb/** #断言,路径相匹配的进行路由
#- After=2021-02-11T14:37:54.926+08:00[Asia/Shanghai]
- RemoteAddr=192.168.1.1/24
  • 结果:由于本机地址不在配置的地址内,所以报错

image-20210211155532617

3、基于Cookie

CookieRoutePredicateFactory:接收两个参数,cookie 名字和一个正则表达式。 判断请求cookie是否具有给定名称且值与正则表达式匹配。

1
- Cookie=chocolate, ch.

修改配置文件,设置访问时需要带上的Cookie

1
2
3
4
5
- id: payment_routh2
uri: lb://cloud-payment-service
predicates:
- Path=/payment/lb/** #断言,路径相匹配的进行路由
- Cookie=wuhu,qifei

打开命令行,使用curl进行访问

image-20210211155943876

image-20210211160356405

4、基于请求头Header

HeaderRoutePredicateFactory:接收两个参数,标题名称和正则表达式。 判断请求Header是否具有给定名称且值与正则表达式匹配。

  • 两个参数,一个属性名称和一个正则表达式,当属性值与正则表达式相匹配时执行
1
- Header=X-Request-Id, \d+

修改yml文件

1
2
3
4
5
6
- id: payment_routh2
uri: lb://cloud-payment-service
predicates:
- Path=/payment/lb/** #断言,路径相匹配的进行路由
# 请求头中需要含有X-Request-Id属性,且X-Request-Id属性值需要为正整数
- Header=X-Request-Id, \d+
  • 使用curl进行测试
1
curl http://localhost:9527/payment/lb -H "X-Request-Id:123"
  • 结果

image-20210211161019074

  • 使用curl进行错误示范
1
curl http://localhost:9527/payment/lb -H "X-Request-Id:-123"
  • 结果

image-20210211161133476

5、基于Method请求方法

MethodRoutePredicateFactory:接收一个参数,判断请求类型是否跟指定的类型匹配。

1
- Method=GET

6、基于Path请求路径

PathRoutePredicateFactory:接收一个参数,判断请求的URI部分是否满足路径规则。

最常见的一种断言

1
- Path=/foo/**

7、基于Query请求参数

QueryRoutePredicateFactory :接收两个参数,请求param和正则表达式, 判断请求参数是否具有给定名称且值与正则表达式匹配。

1
- Query=baz, ba.

修改配置文件

  • Query=uname, wuhu断言要求请求中必须有uname参数,且参数值必须为wuhu
1
2
3
4
5
- id: payment_routh2
uri: lb://cloud-payment-service
predicates:
- Path=/payment/lb/** #断言,路径相匹配的进行路由
- Query=uname, wuhu

image-20210211162139906

image-20210211162254068

三、过滤器

3.1、过滤器基本概念

1、作用

过滤器就是在请求的传递过程中,对请求和响应做一些修改

2、生命周期

客户端的请求先经过“pre”类型的filter,然后将请求转发到具体的业务服务,收到业务服务的响应之后,再经过“post”类型的filter处理,最后返回响应到客户端。

pre: 这种过滤器在请求被路由之前调用。我们可利用这种过滤器实现参数校验、权限校验、流量监控、日志输出、协议转换等;

post:这种过滤器在路由到达微服务以后执行。这种过滤器可用做响应内容、响应头的修改,日志的输出,流量监控等。

image-20210211162518791

3、分类

局部过滤器 GatewayFilter:作用在某一个路由上

全局过滤器 GlobalFilter:作用全部路由上

3.2、局部过滤器

1、内置局部过滤器

在SpringCloud Gateway中内置了很多不同类型的网关路由过滤器。具体如下

过滤器工厂作用参数
AddRequestHeader为原始请求添加HeaderHeader的名称及值
AddRequestParameter为原始请求添加请求参数参数名称及值
AddResponseHeader为原始响应添加HeaderHeader的名称及值
DedupeResponseHeader剔除响应头中重复的值需要去重的Header名称及去重策略
Hystrix为路由引入Hystrix的断路器保护HystrixCommand 的名称
FallbackHeaders为fallbackUri的请求头中添加具体的异常信息Header的名称
PrefixPath为原始请求路径添加前缀前缀路径
PreserveHostHeader为请求添加一个preserveHostHeader=true的属性,路由过滤器会检查该属性以决定是否要发送原始的Host
RequestRateLimiter用于对请求限流,限流算法为令牌桶keyResolver、rateLimiter、statusCode、denyEmptyKey、emptyKeyStatus
RedirectTo将原始请求重定向到指定的URLhttp状态码及重定向的url
RemoveHopByHopHeadersFilter为原始请求删除IETF组织规定的一系列Header默认就会启用,可以通过配置指定仅删除哪些Header
RemoveRequestHeader为原始请求删除某个HeaderHeader名称
RemoveResponseHeader为原始响应删除某个HeaderHeader名称
RewritePath重写原始的请求路径原始路径正则表达式以及重写后路径的正则表达式
RewriteResponseHeader重写原始响应中的某个HeaderHeader名称,值的正则表达式,重写后的值
SaveSession在转发请求之前,强制执行WebSession::save 操作
secureHeaders为原始响应添加一系列起安全作用的响应头无,支持修改这些安全响应头的值
SetPath修改原始的请求路径修改后的路径
SetResponseHeader修改原始响应中某个Header的值Header名称,修改后的值
SetStatus修改原始响应的状态码HTTP 状态码,可以是数字,也可以是字符串
StripPrefix用于截断原始请求的路径使用数字表示要截断的路径的数量
Retry针对不同的响应进行重试retries、statuses、methods、series
RequestSize设置允许接收最大请求包的大 小。如果请求包大小超过设置的值,则返回 413 Payload TooLarge请求包大小,单位为字节,默认值为5M
ModifyRequestBody在转发请求之前修改原始请求体内容修改后的请求体内容
ModifyResponseBody修改原始响应体的内容修改后的响应体内容

2、内置局部过滤器的使用

1
2
3
4
5
6
7
routes:
- id: service-edu
uri: lb://service-edu
predicates:
- Path=/user/**, /*/edu/**
filters:
- SetStatus=250 # 修改返回状态码

3.3、全局过滤器

1、内置全局过滤器

image-20210211162756079

内置全局过滤器的使用举例:负载均衡过滤器

1
lb://service-edu

3.4、自定义全局过滤器

定义一个Filter实现 GlobalFilter 和 Ordered接口

1、作用

  • 全局日志记录
  • 统一网关鉴权

2、自定义全局过滤器

编写一个过滤器类,这个过滤器用于记录日志已经鉴权

  • 如果请求中uname参数值为空,则不放行,直接响应406
  • 这个类需要实现GlobalFilter和Ordered接口,并重写两个方法
  • 在getOrder方法中,定义过滤器的优先级,order越低,优先级越高
1
2
3
4
5
6
public interface Ordered {
int HIGHEST_PRECEDENCE = -2147483648;
int LOWEST_PRECEDENCE = 2147483647;

int getOrder();
}
  • 在filter方法中进行日志记录和鉴权操作
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
@Component
public class MyLogGateWayFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
log.info("*************进入自定义全局过滤器,时间为:" + new Date());
String uname = exchange.getRequest().getQueryParams().getFirst("uname");
if(uname == null) {
log.error("********用户名为null,非法用户!");
//给响应对象返回一个状态码:请求不被接受
exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE);
return exchange.getResponse().setComplete();
}
//放行,进入到下一个过滤器
return chain.filter(exchange);
}

/***
* 此方法用于定义这个过滤器在过滤器链中的位置
* 返回的order越小优先级越高
* @return
*/
@Override
public int getOrder() {
return 0;
}
}
  • 测试

访问http://localhost:9257/payment/lb

image-20210211164137410

image-20210211164206315

访问http://localhost:9257/payment/lb,带上参数uname

image-20210211164402908

四、在线教育项目整合GateWay

注意:gateway底层使用的是webflux会与web冲突,所以在gateway中不要引入spring-boot-starter-web依赖

4.1、统一网关鉴权

我们需要在微服务网关中自定义一个全局过滤器,统一处理需要鉴权的服务

1、鉴权逻辑描述

  • 当客户端第一次请求服务时,服务端对用户进行信息认证(登录)
  • 认证通过,将用户信息进行加密形成token,返回给客户端
  • 作为登录凭证以后每次请求,客户端都携带认证的token
  • 服务端对token进行解密,判断是否有效

image-20210211165219298

对于验证用户是否已经登录鉴权的过程可以在网关统一检验。检验的标准就是请求中是否携带token凭证以及token的正确性。

下面的我们自定义一个GlobalFilter,去校验所有的请求参数中是否包含“token”,如果不包含请求参数“token”则不转发路由,否则执行正常的逻辑。

2、创建自定义过滤器

  • 如果token为空或者token不合法,则不放行。
  • 这个过滤器的order为0
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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
@Component
public class AuthGlobalFilter implements GlobalFilter, Ordered {

@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
String path = request.getURI().getPath();

//谷粒学院api接口,校验用户必须登录
AntPathMatcher antPathMatcher = new AntPathMatcher();
if(antPathMatcher.match("/api/**/auth/**", path)) {
List<String> tokenList = request.getHeaders().get("token");

//没有token
if(null == tokenList) {
ServerHttpResponse response = exchange.getResponse();
return out(response);
}

//token校验失败
Boolean isCheck = JwtUtils.checkJwtTToken(tokenList.get(0));
if(!isCheck) {
ServerHttpResponse response = exchange.getResponse();
return out(response);
}
}

//放行
return chain.filter(exchange);
}

//定义当前过滤器的优先级,值越小,优先级越高
@Override
public int getOrder() {
return 0;
}

private Mono<Void> out(ServerHttpResponse response) {

JsonObject message = new JsonObject();
message.addProperty("success", false);
message.addProperty("code", 28004);
message.addProperty("data", "");
message.addProperty("message", "鉴权失败");
byte[] bytes = message.toString().getBytes(StandardCharsets.UTF_8);
DataBuffer buffer = response.bufferFactory().wrap(bytes);
//指定编码,否则在浏览器中会中文乱码
response.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
//输出http响应
return response.writeWith(Mono.just(buffer));
}
}

3、修改前端

guli-site的utils/request.js中修改响应过滤器 ,添加分支:

  • 如果响应码为20004,则证明认证失败,用户没有登录,此时让用户跳转到登录页
1
2
3
4
else if (res.code === 28004) { // 鉴权失败
window.location.href = '/login'
return
}
  • 修改pages/login.vue的submitLogin方法:登录后回到原来的页面
1
2
3
4
5
6
7
// 跳转到首页
// window.location.href = '/'
if (document.referrer.indexOf('register') !== -1) {
window.location.href = '/'
} else {
history.go(-1)
}

4.2、统一处理跨域问题

1、什么是跨域?

当一个请求url的协议、域名、端口三者之间任意一个与当前页面url不同即为跨域

当前页面url被请求页面url是否跨域原因
http://www.test.com/http://www.test.com/index.html同源(协议、域名、端口号相同)
http://www.test.com/https://www.test.com/index.html跨域协议不同(http/https)
http://www.test.com/http://www.baidu.com/跨域主域名不同(test/baidu)
http://www.test.com/http://blog.test.com/跨域子域名不同(www/blog)
http://www.test.com:8080/http://www.test.com:7001/跨域端口号不同(8080/7001)

2、Spring Boot中解决跨域的几种方法

在gateway中创建一个配置类,这个配置类用于解决跨域问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Configuration
public class CorsConfig {
@Bean
public CorsWebFilter corsFilter() {
CorsConfiguration config = new CorsConfiguration();
config.addAllowedMethod("*");
config.addAllowedOrigin("*");
config.addAllowedHeader("*");

UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(new PathPatternParser());
source.registerCorsConfiguration("/**", config);

return new CorsWebFilter(source);
}
}

在Spring Boot控制器中添加@CrossOrigin注解,这个注解可以加在类上或者方法

1
2
3
4
5
6
@Slf4j
@CrossOrigin
@RestController
@Api(tags = "课程分类管理")
@RequestMapping("/admin/edu/subject")
public class SubjectController {

说明,这两种方法只取一种使用即可,如果使用了配置类,那么最好将控制器上的注解删去。

参考教程如下: