上一篇:2T架构师学习资料干货分享

来源:酒香逢
地址:www.cnblogs.com/fnz0/p/15803011.html

当你遇到跨域问题,不要立刻就选择复制去尝试,请详细看完这篇文章再处理,我相信它能帮到你。

分析前准备:

前端网站地址:http://localhost:8080

服务端网址:http://localhost:59200

首先保证服务端是没有处理跨域的,其次,先用postman测试服务端接口是正常的

当网站8080去访问服务端接口时,就产生了跨域问题,那么如何解决?接下来我把跨域遇到的各种情况都列举出来并通过nginx代理的方式解决(后台也是一样的,只要你理解的原理)。

跨域主要涉及4个响应头:

  • Access-Control-Allow-Origin 用于设置允许跨域请求源地址 (预检请求和正式请求在跨域时候都会验证)

  • Access-Control-Allow-Headers 跨域允许携带的特殊头信息字段 (只在预检请求验证)

  • Access-Control-Allow-Methods 跨域允许的请求方法或者说HTTP动词 (只在预检请求验证)

  • Access-Control-Allow-Credentials 是否允许跨域使用cookies,如果要跨域使用cookies,可以添加上此请求响应头,值设为true(设置或者不设置,都不会影响请求发送,只会影响在跨域时候是否要携带cookies,但是如果设置,预检请求和正式请求都需要设置)。不过不建议跨域使用(项目中用到过,不过不稳定,有些浏览器带不过去),除非必要,因为有很多方案可以代替。

网上很多文章都是告诉你直接Nginx添加这几个响应头信息就能解决跨域,当然大部分情况是能解决,但是我相信还是有很多情况,明明配置上了,也同样会报跨域问题。

什么是预检请求?

当发生跨域条件时候,览器先询问服务器,当前网页所在的域名是否在服务器的许可名单之中,以及可以使用哪些HTTP动词和头信息字段。只有得到肯定答复,浏览器才会发出正式的XMLHttpRequest请求,否则就报错。如下图

开始动手模拟:

Nginx代理端口:22222 ,配置如下

server {listen       22222;server_name  localhost;location  / {proxy_pass  http://localhost:59200;}
}

测试代理是否成功,通过Nginx代理端口2222再次访问接口,可以看到如下图通过代理后接口也是能正常访问

接下来开始用网站8080访问Nginx代理后的接口地址,报错情况如下↓↓↓

情况1:

Access to XMLHttpRequest at 'http://localhost:22222/api/Login/TestGet' from origin 'http://localhost:8080' 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.

通过错误信息可以很清晰的定位到错误(注意看标红部分)priflight说明是个预请求,CORS 机制跨域会首先进行 preflight(一个 OPTIONS 请求), 该请求成功后才会发送真正的请求。这一设计旨在确保服务器对 CORS 标准知情,以保护不支持 CORS 的旧服务器

通过错误信息,我们可以得到是预检请求的请求响应头缺少了 Access-Control-Allow-Origin,错哪里,我们改哪里就好了。修改Nginx配置信息如下(红色部分为添加部分),缺什么就补什么,很简单明了

server {listen       22222;server_name  localhost;location  / {add_header Access-Control-Allow-Origin 'http://localhost:8080';proxy_pass  http://localhost:59200;}
}

哈哈,当满怀欢喜的以为能解决后,发现还是报了同样的问题

不过我们的配置没什么问题,问题在Nginx,下图链接http://nginx.org/en/docs/http/ngx_http_headers_module.html

add_header 指令用于添加返回头字段,当且仅当状态码为图中列出的那些时有效。如果想要每次响应信息都携带头字段信息,需要在最后添加always(经我测试,只有Access-Control-Allow-Origin这个头信息需要加always,其他的不加always也会携带回来),那我们加上试试

server {listen       22222;server_name  localhost;location  / {add_header Access-Control-Allow-Origin 'http://localhost:8080' always;proxy_pass  http://localhost:59200;}
}

修改了配置后,发现生效了,当然不是跨域就解决了,是上面这个问题已经解决了,因为报错内容已经变了。

情况2:

Access to XMLHttpRequest at 'http://localhost:22222/api/Login/TestGet' from origin 'http://localhost:8080' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: It does not have HTTP ok status.

通过报错信息提示可以得知,是跨域浏览器默认行为的预请求(option请求)没有收到ok状态码,此时再修改配置文件,当请求为option请求时候,给浏览器返回一个状态码(一般是204)

server {listen       22222;server_name  localhost;location  / {add_header Access-Control-Allow-Origin 'http://localhost:8080' always;if ($request_method = 'OPTIONS') {return 204;}proxy_pass  http://localhost:59200;}
}

当配置完后,发现报错信息变了

情况3:

Access to XMLHttpRequest at 'http://localhost:22222/api/Login/TestGet' from origin 'http://localhost:8080' has been blocked by CORS policy: Request header field authorization is not allowed by Access-Control-Allow-Headers in preflight response.

意思就是预请求响应头Access-Control-Allow-Headers中缺少头信息authorization(各种情况会不一样,在发生跨域后,在自定义添加的头信息是不允许的,需要添加到请求响应头Access-Control-Allow-Headers中,以便浏览器知道此头信息的携带是服务器承认合法的,我这里携带的是authorization,其他的可能是token之类的,缺什么加什么),知道了问题所在,然后修改配置文件,添加对应缺少的部分,再试试

server {listen       22222;server_name  localhost;location  / {add_header Access-Control-Allow-Origin 'http://localhost:8080' always;if ($request_method = 'OPTIONS') {add_header Access-Control-Allow-Headers 'authorization'; #为什么写在if里面而不是接着Access-Control-Allow-Origin往下写?因为这里只有预检请求才会检查return 204;}proxy_pass http://localhost:59200;
}
}

此时发现报错问题又回到了情况1

经测试验证,只要if ($request_method = 'OPTIONS') 里面写了 add_header ,当为预检请求时外部配置的都会失效,为什么?↓↓。

官方文档是这样说的:

There could be several add_header directives. These directives are inherited from the previous level if and only if there are no add_header directives defined on the current level.

意思就是当前层级无 add_header 指令时,则继承上一层级的add_header。相反的若当前层级有了add_header,就应该无法继承上一层的add_header。

配置修改如下:

server {listen       22222;server_name  localhost;location  / {add_header Access-Control-Allow-Origin 'http://localhost:8080' always;if ($request_method = 'OPTIONS') {add_header Access-Control-Allow-Origin 'http://localhost:8080';add_header Access-Control-Allow-Headers 'authorization';return 204;}proxy_pass  http://localhost:59200;}
}

此时改完发现跨域问题已经解决了,

不过以上虽然解决了跨域问题,但是考虑后期可能Nginx版本更新,不知道这个规则会不会被修改,考虑到这样的写法可能会携带上两个 Access-Control-Allow-Origin ,这种情况也是不允许的,下面会说到。所以配置适当修改如下:

server {listen       22222;server_name  localhost;location  / {if ($request_method = 'OPTIONS') {add_header Access-Control-Allow-Origin 'http://localhost:8080';add_header Access-Control-Allow-Headers 'authorization';return 204;}if ($request_method != 'OPTIONS') {add_header Access-Control-Allow-Origin 'http://localhost:8080' always;}proxy_pass  http://localhost:59200;}
}

还没完,继续聊

情况4:

比较早期的API可能只用到了POST和GET请求,而Access-Control-Allow-Methods这个请求响应头跨域默认只支持POST和GET,当出现其他请求类型时候,同样会出现跨域异常。

比如,我这里将请求的API接口请求方式从原来的GET改成PUT,在发起一次试试。在控制台上会抛出错误:

Access to XMLHttpRequest at 'http://localhost:22222/api/Login/TestGet' from origin 'http://localhost:8080' has been blocked by CORS policy: Method PUT is not allowed by Access-Control-Allow-Methods in preflight response.

报错内容也讲的很清楚,在这个预请求中,PUT方法是不允许在跨域中使用的,我们需要改下Access-Control-Allow-Methods的配置(缺什么加上么,这里我只加了PUT,可以自己加全一点),让浏览器知道服务端是允许的

server {listen 22222;server_name localhost;location / {if ($request_method = 'OPTIONS') {add_header Access-Control-Allow-Origin 'http://localhost:8080';add_header Access-Control-Allow-Headers 'content-type,authorization';add_header Access-Control-Allow-Methods 'PUT';#为这么只加在这个if中,不再下面的if也加上?因为这里只有预检请求会校验,当然你加上也没事。return 204;}if ($request_method != 'OPTIONS') {add_header Access-Control-Allow-Origin 'http://localhost:8080' always;}proxy_pass http://localhost:59200;}
}

这里注意一下,改成PUT类型后,Access-Control-Allow-Headers请求响应头又会自动校验content-type这个请求头,和情况3是一样的,缺啥补啥就行了。如果不加上content-type,则会报如下错误。(想简单的话,Access-Control-Allow-Headers和Access-Control-Allow-Methods可以设置为 * ,表示全都匹配。但是Access-Control-Allow-Origin就不建议设置成 * 了,为了安全考虑,限制域名是很有必要的。)

都加上后,问题就解决了,这里报405是我服务端这个接口只开放了GET,没有开放PUT,而此刻我将此接口用PUT方法去请求,所以接口会返回这个状态码。

情况5:

最后再说一种情况,就是后端处理了跨域,就不需要自己在处理了(这里吐槽下,某些后端工程师自己改服务端代码解决跨域,但是又不理解其中原理,网上随便找段代码黏贴,导致响应信息可能处理不完全,如method没添加全,headers没加到点上,自己用的那个可能复制过来的并不包含实际项目所用到的,没有添加options请求返回状态码等,导致Nginx再用通用的配置就会可能报以下异常)

Access to XMLHttpRequest at 'http://localhost:22222/api/Login/TestGet' from origin 'http://localhost:8080' has been blocked by CORS policy: The 'Access-Control-Allow-Origin' header contains multiple values '*, http://localhost:8080', but only one is allowed.

意思就是此刻Access-Control-Allow-Origin请求响应头返回了多个,而只允许有一个,这种情况当然修改配置去掉Access-Control-Allow-Origin这个配置就可以了,不过遇到这种情况,建议Nginx配置和服务端自己解决跨域只选其一。(这里注意如果按我上面的写法,if $request_method = 'OPTIONS' 这个里面的Access-Control-Allow-Origin可不能删除,删除!='OPTIONS'里面的就好了,因为这里如果是预检请求直接就ruturn了,请求不会再转发到59200服务,如果也删除了,就会报和情况1一样的错误。所以为什么说要不服务端代码层面解决跨域,要不就Nginx代理解决,不要混着搞,不然不明白原理的人,网上找一段代码贴就很可能解决不了问题)

↓ ↓ ↓ ↓ ↓

再贴一份完整配置(*号根据自己‘喜好’填写):

server {listen       22222;server_name  localhost;location  / {if ($request_method = 'OPTIONS') {add_header Access-Control-Allow-Origin 'http://localhost:8080';add_header Access-Control-Allow-Headers '*';add_header Access-Control-Allow-Methods '*';add_header Access-Control-Allow-Credentials 'true';return 204;}if ($request_method != 'OPTIONS') {add_header Access-Control-Allow-Origin 'http://localhost:8080' always;add_header Access-Control-Allow-Credentials 'true';}proxy_pass  http://localhost:59200;}
}

或者:

server {listen       22222;server_name  localhost;location  / {add_header Access-Control-Allow-Origin 'http://localhost:8080' 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:59200;}
}

最后,这是一篇解决跨域遇到问题解决问题的过程,如果认真看完了,我相信应该都能很容易的理解,并且在实际使用中自己解决该问题,希望能帮助到大家,以上内容都是自己理解自己测试码出来的,如有理解不对的地方,望大家指正。

感谢您的阅读,也欢迎您发表关于这篇文章的任何建议,关注我,技术不迷茫!小编到你上高速。

· END ·

最后,关注公众号互联网架构师,在后台回复:2T,可以获取我整理的 Java 系列面试题和答案,非常齐全。

正文结束

推荐阅读 ↓↓↓

1.救救大龄码农!45岁程序员在国务院网站求助总理!央媒网评来了...

2.如何才能成为优秀的架构师?

3.从零开始搭建创业公司后台技术栈

4.程序员一般可以从什么平台接私活?

5.37岁程序员被裁,120天没找到工作,无奈去小公司,结果懵了...

6.IntelliJ IDEA 2019.3 首个最新访问版本发布,新特性抢先看

7.这封“领导痛批95后下属”的邮件,句句扎心!

8.15张图看懂瞎忙和高效的区别!

Nginx 轻松搞定跨域问题!相关推荐

  1. 几行代码轻松搞定跨系统传递 traceId

    同样是新项目开发的笔记,因为使用的是分布式架构,涉及到各个系统之间的交互 这时候就会遇到一个很常见的问题: 单个系统是集群部署,日志分布在多台服务器上: 多个系统的日志在多台机器,但是一次请求,查日志 ...

  2. 轻松搞定JSONP跨域请求

    轻松搞定JSONP跨域请求 一.同源策略 要理解跨域,先要了解一下"同源策略".所谓同源是指,域名,协议,端口相同.所谓"同源策略",简单的说就是基于安全考虑, ...

  3. 【微服务】之六:轻松搞定SpringCloud微服务-API网关zuul

    通过前面几篇文章的介绍,我们可以轻松搭建起来微服务体系中比较重要的几个基础构建服务.那么,在本篇博文中,我们重点讲解一下,如何将所有微服务的API同意对外暴露,这个就设计API网关的概念. 本系列教程 ...

  4. 教你用一条SQL搞定跨数据库查询难题

    导读 日前,某电商用户由于业务发展迅猛,访问量极速增长,导致数据库容量及性能遭遇瓶颈.为降低数据库大小,提升性能,用户决定对架构进行垂直拆分.根据不同的表来进行拆分,对应用程序的影响也更小,拆分规则也 ...

  5. 轻松搞定vmware + win2003Cluste

    作者:网络  来源:网络  发布时间:2007-3-5 16:44:15  发布人:admin 轻松搞定vmware + win2003Cluster <?xml:namespace prefi ...

  6. 网站后台没有提示声怎么办_收藏 | 没有 PS 怎么办?10个在线作图网站,轻松搞定图片设计...

    求职技巧 | 职业技能 | 通关考试,关注公众号:职域go Photoshop 已经成为大多数工作需要用到的工具,很多时候我们都要用到它来满足一些简单的排版.图片处理.尺寸调整或者是做一些简单的效果. ...

  7. 网站被恶意镜像怎么办 php一段代码轻松搞定(全面版)

    有时候你会发现,你在搜索引擎输入网站名称的时候,出来的网站信息是你们的,但是域名却是一个陌生的,这种情况可以基本确定网站被镜像了,那么究竟什么叫网站被镜像? 恶意镜像,也叫恶意克隆,恶意解析,是指有人 ...

  8. 教你如何轻松搞定云上打印管理

    摘要:加快自主创新,满足数字化用户多场景文印需求. 本文分享自华为云社区<有了司印云打印,云上打印管理轻松搞定!>,作者:云商店 . 作为与职场和个人办公息息相关的工作场景,打印长期以来都 ...

  9. 熟练掌握skynet,后端开发丨游戏开发轻松搞定丨多线程丨多进程

    掌握skynet,后端开发原来如此简单 1. 多线程 2. 多进程 3. actor模型-skynet 4. csp模型 [技术分享篇]熟练掌握skynet,后端开发丨游戏开发轻松搞定丨多线程丨多进程 ...

  10. php镜像网页,网站被恶意镜像怎么办 php一段代码轻松搞定(全面版)

    有时候你会发现,你在搜索引擎输入网站名称的时候,出来的网站信息是你们的,但是域名却是一个陌生的,这种情况可以基本确定网站被镜像了,那么究竟什么叫网站被镜像? 恶意镜像,也叫恶意克隆,恶意解析,是指有人 ...

最新文章

  1. 6月机器学习热文TOP10,精选自1400篇文章
  2. vlan 二三层转发
  3. 回归分析残差不满足正态分布_线性回归思路梳理!精华必看!
  4. servlet的注解开发
  5. 同一网段计算机无法共享打印机,Win7同一个局域网内共享打印机不成功的修复方法...
  6. 字符串循环右移的一道题目
  7. oracle自带的sql语言环境变量,Oracle技术网—SQL*Plus系统环境变量有哪些?如何修改?...
  8. pytorch张量操作基础
  9. virtualbox使用手记
  10. Bailian4100 进程检测【排序】
  11. 设计、定义并实现Complex类
  12. jQuery实现滑动门效果
  13. 平面关系:平行,垂直,夹角判定
  14. Android 知识点 250 —— screencap截屏指令
  15. linux如何查看读写权限,Linux系统下如何查看及修改文件读写权限
  16. android6.0程序未安装,Android Studio出包在6.0系统上提示应用未安装的解决
  17. 上个月一位朋友买到翻新N80手机 淘宝充斥奸商假货 【该奸商为四钻】
  18. ASP.NET Core MVC 中的模型验证
  19. oracle mysql 卸载_Oracle数据库卸载步骤
  20. K-Means聚类算法---C++

热门文章

  1. VegasMovie Studio无法安装怎么办?
  2. 十进制转化八进制,十六进制
  3. SDNU 1170.津津的储蓄计划
  4. python函数的参数细节
  5. pg数据库表接口和数据导出
  6. 【SQLAlchemy】SQLAlchemy技术文档(中文版)(上)
  7. 使用SoapUI测试Web Service
  8. Bootstrap3 表单-输出内联表单
  9. 微软笔试题 《Image Encryption》
  10. VBA读取word中的内容到Excel中