前后端交互——CORS

起因

今天(即2022年11月26日),本人第一次尝试让自己写的前端去访问自己写的后端,不再通过 Apifox 的方法

发展

从下午2:30开始,本人正在学习使用 axios,但是查阅资料后发现,大多数的教程使用的都是json-server这个库,考虑到本人的项目是一个前后端分离的项目(一个人承受了所有),本人在简单通过json-server了解了 axios 的使用之后,觉得开始使用自己的 Java 后端。

简单修改 url 之后发送请求,果然不出意外的出意外了:浏览器报错

No ‘Access-Control-Allow-Origin‘ header is present on the requested resource

意思是没有 ACAO 这个 header

此时来到了下午四点

通过 Google,本人了解到这是 CORS 的问题,CORS 本身是一个浏览器的保护机制

CORS

全名是 Cross-Origin Resource Sharing,是一个基于 HTTP-header 的机制,它通过允许服务器标示除了它自己以外的其它源(域、协议和端口),使得浏览器允许这些 origin 访问加载自己的资源。

说人话就是确保浏览器向服务器传递过去的东西浏览器会收,浏览器传递回来的东西会是对的;同时也会确认浏览器访问的 url 和服务器同源(如果不是同源就有可能会是网络攻击者的信息)

本人的浏览器和服务器的端口是不一样的,所以会被 CORS 拦截,毕竟安全第一

由于浏览器的 CORS 是浏览器自己自动进行的,所以解决问题的关键在于让后端服务器实现 CORS 接口。只要服务器实现了CORS接口,就可以跨源通信


而出现这个问题,是后端在返回数据的时候,headers 栏没有添加 ACAO 这个 header。

继续网上冲浪,在B站找到了3种解决办法:JSONP, 修改 SpringBoot 配置文件,Nginx 反向代理

其中 JSONP 技术已经过时

而修改 SpringBoot 配置文件是本人非常不乐意的事情,本人对于 SpringBoot 只处于一个会用的阶段,如果因为配置文件出现 Bug,本人是无能为力的

最后,本人选择了非常成熟且应用广泛的 Nginx 反向代理

Nginx反向代理解决问题的原理

作为一个中间件,我们的所有 Request 和 Respond 都会经过 Nginx 服务端,这就意味着,我们可以把缺失的 header 在 Nginx 中补上再发给浏览器


理想很美满,现实很骨感

本人在五点的时候成功安装好 Nginx,启动服务后报错 10013,查询后发现是端口占用,但是当检查系统 2222(本人设置的端口)在占用时,没有任何返回

本人懵逼了

再次检查 nginx.conf 文件后发现,默认的 server 端口是 80,而本人在注释掉默认配置信息的时候没有把头尾注释掉,如下

server{
    #
    #
}

删除后 start nginx ,一切正常,除了通过浏览器向后端发送请求还是不行,一模一样的错误。

进一步查找资料,有人说 add_header可以多写几个,并且给出了合理的依据,于是本人按照他的建议添加了如下配置:

add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS, PUT';
add_header Access-Control-Allow-Headers 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization';
if ($request_method = 'OPTIONS') {
    return 204;
}

本人看下面的东西都带 ‘’,便认为上面的 *是作者笔误之类的,就顺手给它也加上了

这时候错误提示变了

Request header field access-control-allow-origin is not allowed by Access-Control-Allow-Headers in preflight response.

意思是,本人发送的请求没通过,原因是 Access-Control-Allow-Headers 阻止了本人的请求

本人转念一想,刚刚是不是在配置文件里面写了跟它有关的,就把它注释了试试

错误又变了

The 'Access-Control-Allow-Origin' header contains multiple values '*, *', but only one is allowed.

它说本人 Access-Control-Allow-Origin 这个 header 里面写了两个 *,本人疑惑了,本人不是只写了一个吗,本人认为是本人在后端服务器用于比较时候留的一个注解 @CrossOrigin 在 Nginx 反代之前就添加了一个 Access-Control-Allow-Origin: * ,本人尝试了另外一个没有注解的接口,报错一样


此时已经6:40,本人还没吃晚饭,但此时本人觉得胜利在望

排除掉 Java 后端的错误,那么真相只有一个—— Nginx 服务在本人的 Response 里面添加了两个Access-Control-Allow-Origin: *,本人开始仔细比对别人配置文件中 add_header Access-Control-Allow-Origin *,发现了问题所在,之前添加的 ‘’

本人把它删除之后重新测试

Java 后端成功接受到了前端的请求,并抛出了空指针异常

难得一次看到空指针异常的时候会如此兴奋

在网上查询了 axios 使用 GET 请求的方式,原来参数用的关键字是 param 而不是 data,别问本人为什么本人写上去就是 data,问就是先学的 “Vue + Uni-app”

最后一次修改,真的是最后一次修改了:

看见这个 status: 200 了吗,本人等了一个下午啊

总结

浏览器的 CORS 保护机制阻止了它向后端服务器发送请求,也阻止了后端服务器的回复

通过 Nginx 服务的反向代理,在浏览器和后端服务器之间起到一个中转的作用,成功地在后端的回复报文里面添加了 Access-Control-Allow-Origin(ACAO)这个 header,也成功地安全地实现了前后端的交互


吃饭去了

Be a Neutral Listener, Dialectical Thinker, and Practitioner of Knowledge