1、背景
在前后端分离的微服务项目中,前端给后端发送请求都是从一个配置好的默认路径发送给对应服务的,如renren-fast-vue,在项目的staic/config/index.js文件中,我们可以观察到默认路径如下:
;(function () {
window.SITE_CONFIG = {};
// api接口请求地址(默认路径)
window.SITE_CONFIG['baseUrl'] = 'http://localhost:8080/renren-fast';
// cdn地址 = 域名 + 版本号
window.SITE_CONFIG['domain'] = './'; // 域名
window.SITE_CONFIG['version'] = ''; // 版本号(年月日时分)
window.SITE_CONFIG['cdnUrl'] = window.SITE_CONFIG.domain + window.SITE_CONFIG.version;
})();
即默认访问本机的8080端口下的renren-fast项目,而在微服务项目中,前端会去调用多个微服务项目的接口,不可能只访问这一个地址,为了解决这个问题,就有了api网关,前端只需要往api网关项目的地址发送请求,有api网关去根据请求信息去访问对应的微服务项目就可以了
架构图如下:
2、api网关的实现,以人人开源项目为例
注:这里演示将renren-fast-vue的请求交由网关处理并路由到renren-fast后台管理系统的过程
1、首先修改前端项目的默认api地址,将所有请求发送至网关
;(function () {
window.SITE_CONFIG = {};
// api接口请求地址
window.SITE_CONFIG['baseUrl'] = 'http://localhost:88/api';
// cdn地址 = 域名 + 版本号
window.SITE_CONFIG['domain'] = './'; // 域名
window.SITE_CONFIG['version'] = ''; // 版本号(年月日时分)
window.SITE_CONFIG['cdnUrl'] = window.SITE_CONFIG.domain + window.SITE_CONFIG.version;
})();
2、创建springboot网关项目,引入技术栈,并进行基本配置
【1】引入技术栈:
注册中心:spring cloud alibaba nacos
网关 :spring cloud gateway
【2】配置项目的基本信息,端口号与前端默认地址保持一致,网关需要到注册中心获取服务,因此必须配置nacos地址,并在应用启动类上加入注解@EnableDiscoveryClient注册到nacos上
spring: # 应用名称 application: name: mxshop-gateway # nacos地址 nacos: server-addr: 127.0.0.1:8848 # 端口号 server: port: 88
@SpringBootApplication
@EnableDiscoveryClient
public class MxshopGatewayApplication {
public static void main(String[] args) {
SpringApplication.run(MxshopGatewayApplication.class, args);
}
3、网关配置(重点)
前端同一发送请求给网关,那么网关就必须要具有根据请求地址调用对应微服务的功能,对此,spring cloud gateway提供了routes路由配置来实现根据请求地址路由到对应微服务。配置如下:
spring: gateway: # 进行路由配置 routes: # 路由id,声明多个id可配置多个路由 - id: admin_route # renren-fast是微服务应用名,lb是负载均衡,若有多个同名微服务会进行负载均衡 uri: lb://renren-fast # 断言,匹配以api为开始的地址片段,匹配起始点是端口号后,匹配成功则路由到上方的uri predicates: - Path=/api/** # 进行地址转换,内容较多,下方说明 filters: - RewritePath=/api/(?<segment>/?.*), /renren-fast/$\{segment}
filters是地址转换功能,为什么要进行地址转换呢?我们以登录界面验证码功能介绍
在获取验证码时,前端会通过: http://localhost:88/api/xxx.jpg 发送请求,接收到该请求的网关会请求以下地址的资源:
http://localhost:8080/api/xxx.jpg
注:8080是renren-fast的端口号
可实际上xxx.jpg资源是在以下地址的
http://localhost:8080/renren-fast/xxx.jpg
所以如果不对地址进行转换,那么浏览器的请求将会变成404,找不到资源,于是就有了filters,通过定义规则,将api转换成renren-fast,即
filters: - RewritePath=/api/(?<segment>/?.*), /renren-fast/$\{segment}
红色部分表示左边替换成右边,蓝色部分则表示后续地址片段不变,也即xxx.jpg保持不变,这样就完成地址转换了,需要注意的是,这里的地址转换是对网关请求地址的转换,浏览器看到的请求地址是下面这个,并且是完全没有问题的
注:这里是我完成所有配置的截图,先要达到这个效果还得进行下面的操作
在完成路由配置和地址转换之后,网关是会找不到对应资源,会404。为什么呢?打开f12开发工具,在控制台上可以看到以下信息:
Access to XMLHttpRequest at ‘http://localhost:88/api/sys/login‘ from origin ‘http://localhost:8001‘ has been blocked by CORS policy: Response to preflight request doesn’t pass access control check: No ‘Access-Control-Allow-Origin’ header is present on the requested resource.
大致意思是从8001(前端项目)端口请求88端口的资源被CORS阻止了,也就是说要解决这个问题,首先得了解CORS,也即本文的第二个知识点,跨域
什么是跨域?
跨域:指的是浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的,是浏览器对javascript施加的安全限制。
浏览器的哪些同源策略会引起跨域?
详情可见:跨源资源共享(CORS) – HTTP | MDN
本文中,引起跨域的原因是浏览器基于同源策略使用CORS对88端口(api网关)与8080端口(renren-fast)的通信进行了阻止,也即从88端口访问8080端口是不被允许的
要解决这个问题,可以在api网关项目进行配置来允许跨站请求,步骤如下:
1、创建配置类
2、在配置类中加入以下配置
package com.mx.mxshop.gateway.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.reactive.CorsWebFilter;
import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;
//声明配置类
@Configuration
public class CorsConfig {
//注入容器
@Bean
public CorsWebFilter corsWebFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration configuration = new CorsConfiguration();
//允许所有请求头
configuration.addAllowedHeader("*");
//允许所有请求方式,如get,post等
configuration.addAllowedMethod("*");
//允许所有请求来源
configuration.addAllowedOrigin("*");
//允许携带cookie跨域
configuration.setAllowCredentials(true);
source.registerCorsConfiguration("/**", configuration);
return new CorsWebFilter(source);
}
}
这样就解决了跨域的问题,由于renren-fast项目也对跨站请求进行了配置,需要将renren-fast的配置删除:
/**
* Copyright (c) 2016-2019 人人开源 All rights reserved.
*
* https://www.renren.io
*
* 版权所有,侵权必究!
*/
package io.renren.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class CorsConfig implements WebMvcConfigurer {
// @Override
// public void addCorsMappings(CorsRegistry registry) {
// registry.addMapping("/**")
// .allowedOrigins("*")
// .allowCredentials(true)
// .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
// .maxAge(3600);
// }
}
如果不对renren-fast跨站请求配置删除,会有以下错误,并登录不上:
Access to XMLHttpRequest at ‘http://localhost:88/api/sys/login’ from origin ‘http://localhost:8001’ has been blocked by CORS policy: The ‘Access-Control-Allow-Origin’ header contains multiple values ‘http://localhost:8001, http://localhost:8001’, but only one is allowed.