记录一次跨域问题的排查

1、环境

前端是单独部署在A服务器上的,后端gateway和Java微服务是部署在B服务器上的,A服务器上的Nginx配置了请求转发,整个请求链路如下:浏览器->A服务器Nginx->B服务器gateway->B服务器java微服务

2、遇到问题

前端同学本地调试过程中,出现了跨域的问题。理论上前端部署在A服务器上,Nginx也部署在A服务器上,前端访问的经过Nginx转发的地址,不会出现跨域问题的,但是问题确实出现了,正好学习了解一下跨域产生的原因和常规的处理方法。

3、跨域产生的原因

跨域是由浏览器的同源策略导致的,目的是防止攻击,确保网站访问的是自己受信任的域名,当协议、ip、端口三者任意一个不一样时,会认为是跨域。

浏览器解决跨域的方式就是CORS(跨域资源共享),浏览器能访问不同源的资源,由资源服务端决定,如果资源端允许当前origin的请求,则在请求头Access-Control-Allow-Origin返回请求头origin本身或者*即可,流程如下:

跨域主要设计到4个响应头:

Access-Control-Allow-Origin 用于设置允许跨域请求源地址 (预检请求和正式请求在跨域时候都会验证)
Access-Control-Allow-Headers 跨域允许携带的特殊头信息字段 (只在预检请求验证)
Access-Control-Allow-Methods 跨域允许的请求方法或者说HTTP动词 (只在预检请求验证)
Access-Control-Allow-Credentials 是否允许跨域使用cookies,如果要跨域使用cookies,可以添加上此请求响应头,值设为true(设置或者不设置,都不会影响请求发送,只会影响在跨域时候是否要携带cookies,但是如果设置,预检请求和正式请求都需要设置)。
跨域请求时会先发送预检请求,浏览器首先会询问服务器,当前网页所在的域名是否在服务器的许可列表中,以及可以使用的请求头和请求方法。若得到肯定的答复,才会发送正式请求Xhr请求,否则报错

从上面的时序图可以知道,关键点就是响应头里面需要增加Access-Control-Allow-Origin,所以解决方法就有如下三种:

3.1、Nginx的反向代理

  1. 如果Nginx和客户端在同一域名下,通过Nginx反向代理服务端,客户端访问的Nginx的地址,则不存在跨域问题。
  2. 如果Nginx和客户端不在同一域名下,可以在Nginx上配置响应头,解决跨域问题,配置如下:
server {
listen 8080;
server_name localhost;
location / {
add_header Access-Control-Allow-Origin 'http://localhost:5501' always;
add_header Access-Control-Allow-Headers '*';
add_header Access-Control-Allow-Methods '*';
add_header Access-Control-Allow-Credentials 'true';
if ($request_method = 'OPTIONS') {
return 204;
}
proxy_pass http://localhost:9090;
}
}

3.2、gateway统一增加响应头

如果请求没有经过Nginx,或者Nginx没有配置跨域,则可以在gateway配置跨域,在application.propertity中增加跨域配置,方法如下:

spring:
cloud:
gateway:
globalcors:
#允许options请求
add-to-simple-url-handler-mapping: true
cors-configurations:
'[/**]':
#允许哪些网站的跨域请求
allowedOriginPatterns: "*"
#允许的跨域ajax请求
allowedMethods:
- "GET"
- "POST"
- "DELETE"
- "PUT"
- "OPTIONS"
#允许在请求头中携带的信息
allowedHeaders: "*"
#是否允许携带cookie
allowCredentials: true
#这次跨域检测的有效期
maxAge: 360000

3.3、java微服务解决

 如果Nginx没有配置跨域、gateway也没有配置跨域,则可以在java微服务里面配置,一般的配置也就两种,一种是全局配置,实现addCorsMapping方法和局部的@CrossOrigin注解

3.3.1、重写WebMvcConfigurer接口的默认方法

@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
//是否发送Cookie
.allowCredentials(true)
//放行哪些原始域
.allowedOrigins("*")
.allowedMethods(new String[]{"GET", "POST", "PUT", "DELETE"})
.allowedHeaders("*")
.exposedHeaders("*");
}
}

3.3.2、@CrossOrigin

这个注解可以在类和方法上使用,在类上使用,则类里面所有的Controller接口都支持跨域,在方法上使用,则该方法支持跨域,详细使用方法自行百度。

4、问题解决

本次遇到的问题,是gateway上的application.properyity中的配置项key写错了,导致没有生效,奇怪的是我们的java微服务本身也做了跨域,理论上网关未生效,也是可以支持跨域的,然后我们修改了网关的跨域后,出现了新的问题,由于网关和java微服务都做了跨域,导致响应头重复了(这是正常现象,一条链路上,多个地方处理跨域就会重复),针对这个问题,在网关中针对指定路由中重复的响应头做处理,示例配置如下:

spring:
cloud:
gateway:
routes:
- id: test
uri: lb://test
predicates:
Path=/api/**
filters:
- DedupeResponseHeader=Access-Control-Allow-Origin, RETAIN_FIRST

 RETAIN_FIRST和RETAIN_UNIQUE两种策略,前者是取第一个,后者是键和值相同就去重保留一个,键相同值不同,还是会有两个,一般我们java微服务的请求头是先加进去的,取第一个没有问题。

posted @   浪迹天涯的派大星  阅读(386)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· DeepSeek 开源周回顾「GitHub 热点速览」
点击右上角即可分享
微信分享提示