当前位置: 首页 > 后端技术 > Java

Nginx轻松处理跨域问题,..

时间:2023-04-01 20:43:25 Java

来源:九象峰\地址:https://www.cnblogs.com/fnz0/...遇到跨域问题时,不要选择立即复制尝试,Deal前请详细阅读本文有了它,我相信它能帮到你。分析前准备:前端网站地址:http://localhost:8080服务器网站:http://localhost:59200首先确保服务器不处理跨域,其次使用postman测试服务器接口第一。网站8080访问服务器接口时出现跨域问题,那么如何解决呢?下面我就把跨域遇到的各种情况都罗列出来,通过nginx代理来解决(后台一样,明白原理就行)。跨域主要涉及4个响应头:Access-Control-Allow-Origin用于设置允许跨域请求的源地址(pre-checkrequests和officialrequests会在跨域时进行验证)Access-Control-Allow-Headerscross-domainallowportability特殊的header信息字段(仅用于预检请求验证)Access-Control-Allow-Methods跨域允许的请求方法或HTTP动词(仅用于预检请求验证)Access-Control-Allow-Credentials是否允许跨域使用cookies,如果要跨域使用cookies,可以在这个请求响应头中加上值设置为true(设置与否不影响请求的发送,只影响是否发送跨域时携带cookie,但如果设置,预检请求和生产请求都需要设置)。但是不建议跨域使用(项目中一直使用,但不稳定,部分浏览器无法携带),除非必须,因为有很多替代方案。网上很多文章告诉你,直接在Nginx中加入这些响应头信息,就可以解决跨域问题。当然,大部分情况是可以解决的,但我相信还是有很多情况。明明是配置的,跨域的问题也会报。.什么是预检请求?:当发生跨域情况时,浏览器首先询问服务器当前网页的域名是否在服务器的权限列表中,哪些HTTP动词和头字段可以使用。只有收到肯定的回复,浏览器才会发出正式的XMLHttpRequest请求,否则会报错。如下图开始动手模拟:Nginx代理端口:22222,配置如下server{listen22222;服务器名称本地主机;位置/{proxy_passhttp://localhost:59200;}}测试是否代理成功,再次通过Nginx代理2222端口访问接口,可以看到通过代理后可以正常访问接口如下图。接下来使用网站8080访问Nginx代理后的接口地址。/Lo...'来自'http://localhost:8080'已被CORS策略阻止:对预检请求的响应未通过访问控制检查:没有'Access-Control-Allow-Origin'标头存在于请求的资源。通过报错信息可以很清楚的定位到错误(注意红色部分)。预检说明是预先请求。CORS机制跨域会先进行preflight(一次OPTIONS请求),请求成功后才会发送真正的请求。问。此设计旨在确保服务器了解CORS标准,以保护不支持CORS的旧服务器。通过报错信息我们可以得知preflight请求的请求响应头缺少Access-Control-Allow-Origin。哪里不对,我们改成Anywhere就可以了。修改Nginx配置信息如下(红色部分为新增部分),缺的补上,很简单明了server{listen22222;服务器名称本地主机;location/{add_headerAccess-Control-Allow-Origin'http://localhost:8080';proxy_passhttp://localhost:59200;}}哈哈,当我满心欢喜,以为可以解决的时候,发现报同样的问题,但是我们的配置没有问题。问题出在Nginx上。下面的链接是http://nginx。org/en/docs/http...add_header指令用于添加返回标头字段当且仅当状态代码是图中列出的那些时。如果要为每个响应报文携带头域信息,则需要在末尾加上always(经过我的测试,只有Access-Control-Allow-Origin头信息需要加always,其他头信息不用always会被带回去),然后我们添加试试server{listen22222;服务器名称本地主机;location/{add_headerAccess-Control-Allow-Origin'http://localhost:8080'始终;proxy_passhttp://localhost:59200;}}修改配置后,发现生效了,当然不是跨域解决了,上面的问题已经解决了,因为报错的内容变了情况二:访问XMLHttpRequestat'http://localhost:22222/api/Lo...'来自来源'http://localhost:8080'已被CORS策略阻止:对预检请求的响应未通过访问控制检查:它没有HTTP正常状态。从报错信息可以知道是跨域浏览浏览器默认行为的预请求(optionrequest)是没有收到ok状态码的。这个时候修改配置文件。当请求为选项请求时,返回一个状态码(一般为204)给浏览器服务器{listen22222;服务器名称本地主机;location/{add_headerAccess-Control-Allow-Origin'http://localhost:8080'始终;如果($request_method='OPTIONS'){返回204;}proxy_passhttp://localhost:59200;}}当配置完成时,Discovery报错信息变了情况三:从源头'http://localhost:8080'访问XMLHttpRequestat'http://localhost:22222/api/Lo...'hasbeenblockedbyCORSpolicy:Requestheaderfieldauthorization预检响应中的Access-Control-Allow-Headers不允许。表示请求前响应头Access-Control-Allow-Headers中缺少头信息授权(各种情况会有所不同,发生跨域后,在自定义中添加头信息是不允许的,需要在请求响应头中加入Access-Control-Allow-Headers,让浏览器知道携带这个头信息是服务器认可的合法的,我这里携带的是授权,其他的可能是token等,缺什么补什么),知道问题了,那就修改配置文件,补上对应缺的部分,然后试试server{listen22222;服务器名称本地主机;location/{add_headerAccess-Control-Allow-Origin'http://localhost:8080'始终;如果($request_method='OPTIONS'){add_headerAccess-Control-Allow-Headers'授权';#为什么写if而不是遵循Access-Control-Allow-Origin?因为只有pre-check请求才会检查return204;}proxy_passhttp://localhost:59200;}}这时候发现报错问题,又回到情况1,经过测试验证,只要if($request_method='OPTIONS')写ifadd_headeradded,请求preflight时外部配置会失效,为什么?↓↓官方文档说:可以有多个add_header指令。当且仅当当前级别上没有定义add_header指令时,这些指令才从上一级别继承。意思是当当前层没有add_header指令时,会继承上一层的Hierarchicaladd_header。反之,如果当前层有add_header,则应该不能继承上一层的add_header。配置修改如下:server{listen22222;服务器名称本地主机;location/{add_headerAccess-Control-Allow-Origin'http://localhost:8080'始终;如果($request_method='OPTIONS'){add_headerAccess-Control-Allow-Origin'http://localhost:8080';add_headerAccess-Control-Allow-Headers'授权';返回204;}proxy_passhttp://localhost:59200;}}至此,跨域问题已经解决了,不过上面虽然解决了跨域问题,但是考虑到后面可能会更新Nginx版本,不知道会不会修改这条规则。考虑到这种写法可能会携带两个Access-Control-Allow-Origin,这种情况也是不允许的,下面会提到。所以配置适当修改如下:server{listen22222;服务器名称本地主机;location/{if($request_method='OPTIONS'){add_headerAccess-Control-Allow-Origin'http://localhost:8080';add_headerAccess-Control-Allow-Headers'授权';返回204;}if($request_method!='OPTIONS'){add_headerAccess-Control-Allow-Origin'http://localhost:8080'始终;}proxy_passhttp://localhost:59200;}}还没完,继续聊↓↓情况4:早期的API可能只使用POST和GET请求,请求响应头Access-Control-Allow-Methods默认只支持POST和GET跨域。当请求请求类型时,也会出现跨域异常。比如我这里把请求的API接口的请求方式从原来的GET改为PUT,启动后再试。在控制台上会抛出一个错误:AccesstoXMLHttpRequestat'http://localhost:22222/api/Lo...'fromorigin'http://localhost:8080'hasbeenblockedbyCORSpolicy:MethodPUTisnot预检响应中的Access-Control-Allow-Methods允许。报错的内容也很清楚。在这个前置请求中,不允许跨域使用PUT方法。我们需要更改Access-Control-Allow-Methods的配置(缺什么加什么,这里我只加了PUT,大家可以自己加),让浏览器知道服务器是允许的server{listen22222;服务器名称本地主机;location/{if($request_method='OPTIONS'){add_headerAccess-Control-Allow-Origin'http://localhost:8080';add_headerAccess-Control-Allow-Headers'content-type,authorization';add_header访问控制允许方法'PUT';#为什么只加到这个if,不加到后面的if?因为这里只会验证preflightrequest,当然你可以加上。返回204;}if($request_method!='OPTIONS'){add_headerAccess-Control-Allow-Origin'http://localhost:8080'始终;}proxy_passhttp://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方法请求这个接口,所以接口会返回这个状态码。情况五:最后说一下另外一种情况,就是后端处理跨域,不需要自己处理(这里有的后端工程师通过修改服务器代码来解决跨域自己写的,但是不懂原理,随便在网上找了一段代码贴上去,导致响应信息处理不完整,比如方法没有加全,headers没有加到点,自己用的可能是抄的,不包含实际项目,optionsrequest没有添加返回状态码等,导致Nginx使用普通配置可能会报如下异常)AccesstoXMLHttpRequestat'http://localhost:22222/api/Lo...'fromorigin'http://localhost:8080'已被CORS策略阻止:'Access-Control-Allow-Origin'标头包含多个值'*,http://localhost:8080',但只允许一个。意思是此时的Access-Control-Allow-Origin请求返回了多个responseheaders,但是只允许一个。这种情况当然要修改配置去掉Access-Control-Allow-Origin配置。不过这种情况下还是建议配置Nginx和服务端自行解决跨域问题。只选一个。(这里注意,如果按照我上面的写法,if$request_method='OPTIONS'中的Access-Control-Allow-Origin不能删除,直接删除!='OPTIONS'即可,因为如果是预检请求会直接转过来,不会把请求转发给59200服务,如果也删了,会报和case1一样的错误。那为什么说要在服务端代码层面解决跨域,或者使用Nginx代理来解决呢,千万不要混为一谈,不然不懂原理的人可能解决不了问题通过在网上贴一段代码)↓↓↓↓↓贴出一个完整的配置(*号根据自己喜好填写):server{listen22222;服务器名称本地主机;location/{if($request_method='OPTIONS'){add_headerAccess-Control-Allow-Origin'http://localhost:8080';add_headerAccess-Control-Allow-Headers'*';add_header访问控制允许方法'*';add_headerAccess-Control-Allow-Credentials'true';返回204;}if($request_method!='OPTIONS'){add_headerAccess-Control-Allow-Origin'http://localhost:8080'始终;add_headerAccess-Control-Allow-Credentials'true';}proxy_passhttp://localhost:59200;}}或:server{listen22222;服务器名称本地主机;location/{add_headerAccess-Control-Allow-Origin'http://localhost:8080'始终;add_headerAccess-Control-Allow-Headers'*';add_header访问控制允许方法'*';add_headerAccess-Control-Allow-Credentials'true';如果($request_method='OPTIONS'){返回204;}proxy_passhttp://localhost:59200;}}最后,这是一个解决跨域问题和解决问题的过程。如果仔细阅读,相信应该很容易理解,在实际使用中可以自己解决问题。希望对大家有所帮助。以上内容都是我根据自己的测试代码自己理解出来的。如果我的理解有什么不对的地方,希望大家指正。近期热点文章推荐:1.1000+Java面试题及答案(2022最新版)2.棒棒哒!Java协程来了。..3.SpringBoot2.x教程,太全面了!4.不要用爆破爆满画面,试试装饰者模式,这才是优雅的方式!!5.《Java开发手册(嵩山版)》最新发布,赶快下载吧!感觉不错,别忘了点赞+转发!