现在越来越多的项目就算是一个管理后端也偏向于使用前后端分离的部署方式去做, 为了顺应时代的潮流, 一前后端分离就产生了跨域问题, 所以许多同学把跨域和前后端分离项目联系在了一起, 其实跨域产生的原因并不是前后端分离导致的, 那我们一起来看一下, 希望可以靠这一篇文章解答大家所有的跨域问题
一, 跨域产生的条件
使用 xmlHttpRequest, 即我们通常说的 Ajax 请求
浏览器做了这个事
访问的域名不同, 即访问的 html 页面是 a 域名下的, 但内部 JS 发送的 Ajax 请求的目标地址却是 b 域名
以上三个条件缺一不可, 尤其是第三个条件许多做移动端的同学可能都没有听过, 因为移动端爽爽的用各种 http 请求狂发不同的域名, 但是浏览器不允许我们这么做, 为了一个词安全
二, 如何解决跨域问题
解决跨域问题的根本就是要打破上述的三个限制中的任何一个, 我们来看一下逐个击破的方式
JSONP 方式
JSONP 是打破第一重限制, 用了 XMLHttpRequest 就跨域, 那我不用这种方式了, 我们怎么做的, 来看一段 jQuery 的带 JSONP 的 Ajax 请求
- $.Ajax({
- type : "GET",
- url : "http://api.map.baidu.com/geocoder/v2/",
- data:"address = 上海",
- dataType:"jsonp",
- JSONP:"callback",
- jsonpCallback:"showLocation",
- success : function(data){
- alert("成功");
- },
- error : function(data){
- alert("失败");
- }
- });
看似用了 Ajax 请求, 其实内部完全不是那么回事, 多了 JSONP 和 jsonpCallback 选项, 它内部将代码翻译并把页面上的 dom 操作成这样
- <!DOCTYPE HTML>
- <HTML>
- <head>
- <meta charset="utf-8">
- </head>
- <body>
- <script type='text/javascript'>
- // 后端返回直接执行的方法, 相当于执行这个方法, 由于后端把返回的数据放在方法的参数里, 所以这里能拿到 res.
- Windows.showLocation = function(res) {
- console.log(res)
- // 执行 Ajax 回调
- }
- </script>
- <script src='http://api.map.baidu.com/geocoder/v2/?address = 上海 & callback=showLocation'
- type='text/javascript'>
- </script>
- </body>
- </HTML>
这个时候, HTML 页面的 script src 标签回去访问 API.map.baidu.com 的服务端, 由于 script,img 这种标签浏览器是不受 xmlhttprequest 限制的, 可以随意访问, 这个时候对应的后端代码取得 address 参数, 最后根据双方约定好的 callback 参数, 返回一个被包装后的 JSON, 即
- showLocation({
- status: 0,
- result: {
- location: {
- lng: 121.4219317908279,
- lat: 31.361653367912695
- },
- precise: 1,
- confidence: 80,
- comprehension: 99,
- level: "道路"
- }
- })
然后浏览器直接执行了对应的这个 showLocation()... 等等, 这个不就相当于执行了我们上面定义的 Windows.showLocation 方法并且传入了我们需要的 JSON 返回吗, 那我们的 Ajax success 方法里就可以得到这个返回类型了, 并且没有跨域, 是不是很精妙.
CORS
CORS 是一个 W3C 标准, 全称是 "跨域资源共享"(Cross-origin resource sharing) 跨域资源共享 CORS 详解. 这个玩样用于 "破解" 掉浏览器的限制, 说是破解其实也是浏览器认识到了一些头部就放行了的意思, 需要在 http 的 response 内多设置几个头部
Access-Control-Allow-Origin:* 表明允许所有的 origin(浏览器的 HTML 页面路径) 访问, 而并非是同源的 origin
Access-Control-Request-Method:* 表明允许所有的 http request 头, 访问, 因为浏览器在触发如下几个场景会在发送真正的数据前发送 options 这样的预检请求检测, 一旦预检通过后才会发送真正的 get 或 post 数据请求, 这个时候我们按照 cors 的设置就需要允许对应的 method 访问, 触发的几种情况包括
1: 请求的方法不是 GET/HEAD/POST
2:POST 请求的 Content-Type 并非 application/x-www-form-urlencoded, multipart/form-data, 或 text/plain
3: 请求设置了自定义的 header 字段等
Access-Control-Allow-Headers:* 设置所有 header 均可以被允许, 这个配置联通上述的 request method options 检测一起使用, 可以在需要自定义 header 的场景下使用
Access-Control-Allow-Credentials:true 这个参数只有当需要跨域使用 cookie 传递时才需要设置为 true, 并且需要前端 Ajax 配置使用 xhrField:{withCredential:true} 时才能传递 cookie, 另外 Safari 和最新版本的 Chrome 浏览器还需要在设置内放开对应限制, 可参考我的秒杀课程, 当这个参数被设置成 true 时候 Access-Control-Allow-Origin 就不能设置为 *, 否则就变成任何 origin 域都能允许传递 cookie 了, 可将其调整为前端 origin 字段传什么我就用什么
若你使用的是 nginx 反向代理, 则可以直接在 nginx 反向代理上配置
- location /{
- proxy_pass http://backendserver;
- add_header Access-Control-Allow-Methods *;
- add_header Access-Control-Allow-Credentials true;
- add_header Access-Control-Allow-Origin $http_origin;
- add_header Access-Control-Allow-Headers *;
- }
代理法
打破不同源的限制, 我只要让它同源就可以了, 比如要我的静态页面是 http://a.com/index.html 动态 Ajax 请求访问的是 http://b.com/api/***
我只需要将对应的服务部署在不同的机器上, 然后使用一个公共的 c.com 的域名作为 nginx 反向代理的入口域名, 在将静态服务和动态服务分别挂在后面的被代理局域网服务器内, 修改配置
- server{
- listen:80;
- server_name: c.com;
- #静态资源
- location /{
- proxy_pass http://localhost:8080/;
- }
- #Ajax 动态请求
- location /API{
- proxy_pass http://localhost:8081/;
- }
- }
这样就变成同源了
写在最后
第一: 看完点赞, 感谢您对作者的认可;
...
第二: 随手转发, 分享知识, 让更多人学习到;
...
第三: 记得点关注, 每天更新的!!!
...
来源: http://www.bubuko.com/infodetail-3105895.html