解决跨域问题
来源:互联网 发布:mac os x lion系统 编辑:程序博客网 时间:2024/05/22 00:18
解决跨域问题
介绍一下如何解决跨域问题。
目录:
- 解决跨域问题
- 为什么会有跨域问题
- 判断是否同源
- JSONP实现跨域
- CORS跨源资源分享
- CORS与JSONP比较
- 服务器代理实现跨域
为什么会有跨域问题
为了阻止一个页面上的恶意脚本通过页面的DOM对象获得访问另一个页面上敏感信息的权限,浏览器采用了同源策略。
在这个策略下,只有在两个页面有相同的源时,web 浏览器允许一个页面的脚本访问另一个页面里的数据。
浏览器的同源策略又分为两种:
- DOM同源策略:禁止对不同源页面DOM进行操作。这里主要场景是 iframe 跨域的情况,不同域名的 iframe 是限制互相访问的。
- XmlHttpRequest同源策略:禁止使用XHR对象向不同源的服务器地址发起HTTP请求。
同源策略限制的范围:
- 无法读取Cookie、LocalStorage 和 IndexDB 。
- 无法获取DOM 。
- 不能发送 Ajax 请求。
判断是否同源
假设一个 URL 为 http://www.example.com/dir/page.html
,以其为例判断以下 URL 是否同源:
http://www.example.com/dir/page2.html
同源 相同的协议,主机和端口 http://www.example.com/dir2/other.html
同源 相同的协议,主机和端口 http://username:password@www.example.com/dir2/other.html
同源 相同的协议,主机和端口 http://www.example.com:81/dir/other.html
不同源 相同的协议和主机但端口不同 https://www.example.com/dir/other.html
不同源 协议不同 http://en.example.com/dir/other.html
不同源 不同主机 http://example.com/dir/other.html
不同源 不同主机(需要精确匹配) http://www.example.com:80/dir/other.html
待定 端口明确,依赖浏览器实现JSONP实现跨域
jsonp 利用了 <script>
标签中 src 属性能够跨域访问的特性,先定义了一个回调方法,然后将其当作 url 参数的一部分发送到服务端,服务端通过字符串拼接的方式将用户想要的数据包裹在回调方法中,再传回来,返回的 js 脚本直接就会执行了。
<script type="text/javascript"> // 定义一个回调函数函数 function callback(data) { console.log(data); }; // 创建一个脚本,并且告诉服务端端回调函数名叫callback var script = document.createElement('script'); var url = 'http://localhost:3000/jsonp?callback=callback'; script.setAttribute("type","text/javascript"); script.src = url; document.body.appendChild(script);</script>
使用 node.js 写服务端代码响应请求:
/* GET jsonp listing. */router.get('/', function(req, res, next) { // 要返回的数据 var data = { "name": "kaelyn" }; // 把json数据转化成字符串,方便字符串拼接 data = JSON.stringify(data); // 拼接回调函数的字符串 var callback = req.query.callback+'('+data+');'; res.end(callback);});
这样就实现了通过 jsonp 跨域访问:
值得注意的是,回调函数需要是全局的。
req.query.callback 获取 URL 的键名为‘callback’ 的查询参数串。
关于 node.js 的 Express 框架的基本使用可以看看这里。
在前端除了上面的一种写法之外,还有其他写法:
<script type="text/javascript"> function callback(data) { console.log(data); };</script><!--直接插入一个 script 标签--><script type="text/javascript" src="http://localhost:3000/jsonp?callback=callback"></script>
还可以使用 jquery 来实现:
<script type="text/javascript"> $.ajax({ type:"get", url:"http://localhost:3000/jsonp?", dataType: "jsonp", jsonp: "callback", //在一个jsonp请求中重写回调函数的名字(key) jsonpCallback:"showData", //为jsonp请求指定一个回调函数名(value) async:false, success:function(data){ console.log(data); } });</script>
在 AJAX 请求设置中,jsonp 和 jsonpCallback 选项可以不写,jquery都会帮我们写好的,默认直接回调调用请求成功后的回调函数 success 。
如果像上面的代码一样定义,则在服务端接收到的请求 URL 将会加上 ?&callback=showData
,如果我们改了AJAX 请求设置中 jsonp 的值,那么服务端也需要做出一些修改:
var callback = req.query.callback+'('+data+');';
上面代码中的 req.query.callback
要进行相应的修改,因为 URL 中的参数串的键名已经改了,如果还是原来的代码将会获取到 undefined。
虽然是使用了 jquery 帮我们封装好的方法,但是 jsonp 和 ajax 在本质上是不一样的东西,ajax 的核心是通过 XmlHttpRequest 获取非本页内容,而 jsonp 的核心则是动态添加
<script>
标签来调用服务器提供的 js 脚本。
CORS(跨源资源分享)
CORS(Cross-origin resource sharing)是一个W3C标准,是跨源AJAX请求的根本解决方法。
在服务端启用CORS:
//设置跨域访问 app.all('*', function(req, res, next) { res.header("Access-Control-Allow-Origin", "*"); res.header("Access-Control-Allow-Headers", "X-Requested-With"); res.header("Access-Control-Allow-Methods","PUT,POST,GET,DELETE,OPTIONS"); res.header("X-Powered-By",' 3.2.1') res.header("Content-Type", "application/json;charset=utf-8"); next(); });
上面代码的all方法表示所有请求都必须通过该中间件,参数中的“*”表示对所有路径有效。
在网页中发起 ajax 请求:
$.ajax({ type:"get", url:"http://localhost:3000/query", dataType: "json", success:function(data){ console.log(data); }});
结果当然是能成功访问啦:
可以做个比较,如果没有在服务端没有加上上面的允许其他源访问的代码,浏览器将会报错:
服务端返回的 Access-Control-Allow-Origin: * 表明,该资源可以被任意外域访问。如果服务端仅允许来自 http://foo.example
的访问,该首部字段的内容如下:Access-Control-Allow-Origin: http://foo.example
。
如果希望允许多个域名访问,则可以这样设置:
app.all('*', function(req, res, next) { // 允许访问的域名列表 var originList = ["http://localhost:8020", "https://www.baidu.com", "http://www.google.com"]; // 访问的域名 var reqOrigin = req.headers.origin; // 判断访问的域名是否在允许访问的域名列表中 if(!!reqOrigin && originList.indexOf(reqOrigin) != -1){ console.log(reqOrigin); res.header("Access-Control-Allow-Origin", reqOrigin); res.header("Access-Control-Allow-Headers", "X-Requested-With"); res.header("Access-Control-Allow-Methods","PUT,POST,GET,DELETE,OPTIONS"); res.header("X-Powered-By",' 3.2.1') res.header("Content-Type", "application/json;charset=utf-8"); } next(); });
这样我们就可以通过判断访问的域名是否在我们允许的域名列表中,如果存在就允许跨域访问,否则不允许。
Array.prototype.indexOf() 方法返回在数组中可以找到一个给定元素的第一个索引,如果不存在,则返回-1。
关于 CORS 的知识可以看看这里。
CORS与JSONP比较
- 相比 JSONP 只能发 GET 请求,CORS 允许任何类型的请求。
- JSONP 的优势在于支持老式浏览器(IE浏览器不能低于IE10才能兼容 CORS),以及可以向不支持 CORS 的网站请求数据。
服务器代理实现跨域
因为浏览器端的同源策略才产生了跨域问题,所以我们可以使用服务器代理的方法绕开浏览器端:前端向与自己同源的服务器发起请求,该同源服务器再向不同源的服务器发送请求(请求转发),把同源服务器请求的数据再返回给前端就大功告成了。
首先看看端口号为3000的 node.js 服务器的代码:
/* GET home page. */router.get('/', function(req, res, next) { res.sendfile('./views/proxy.html');});router.get('/proxy', function(req, res, next) { var headers = req.headers; var options = { host: 'localhost', port: 5000, path: '/query', method: 'GET', headers: headers }; // 发起 HTTP 请求 var req = http.request(options, function(res) { res.setEncoding('utf8'); res.on('data', function (data) { //从端口号为5000的服务器中获取 data var data = JSON.parse(data); //获取数据后传回浏览器 success(data); }); }); req.on('error', function(e){ console.log("problem with request:" + e.message); }); req.end(); //获取数据后传回浏览器 function success(data){ res.send(data); }});
当开始服务器后访问http://localhost:3000/
,服务器传回一个proxy.html
文件给浏览器:
<!--proxy.html--><!DOCTYPE html><html> <head> <meta charset="UTF-8"> <title>proxy</title> </head> <body> <script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script> <script type="text/javascript"> $.ajax({ type:"get", url:"http://localhost:3000/proxy", dataType: "json", success:function(data){ console.log(data); } }); </script> </body></html>
proxy.html
网页中发起 ajax 请求向http://localhost:3000/proxy
获取数据,然后端口号为3000的服务器接受到请求后再发起 HTTP 请求获取http://localhost:5000/query
的数据。
显然,网页和端口号为3000的服务器就是同源的,这样子 ajax 请求肯定没问题,而且因为服务器端之间不存在跨域问题,所以端口号为3000的服务器向端口号为5000的服务器发送请求也没问题。
再来看看端口号为5000的 node.js 服务器的代码:
router.get('/query', function(req, res, next) { var data = { "name": "kaelyn" } res.json(data);});
就这样,当我们启动两个服务器后,打开浏览器访问http://localhost:3000/
,就可以看到在控制台上打印出了返回的数据,这就是通过服务器代理解决跨域问题的方法。
关于 node.js 的 http.request 方法的知识可以看看这里。
- 解决跨域问题
- 解决跨域问题
- 解决跨域问题
- 解决跨域问题
- 解决跨域问题
- 解决跨域问题
- 解决跨域问题
- 解决跨域问题
- 解决跨域问题
- 解决跨域问题
- 解决跨域问题
- 解决js跨域问题
- 解决javascript跨域问题
- 解决AJAX跨域问题
- 解决js跨域问题
- 解决js跨域问题
- 解决js跨域问题
- php 解决跨域问题
- linux入门笔记GCC编译器
- 贪心中“换”的思想
- 程序运行
- 阿里巴巴Java开发手册插件版安装教程
- 处理 /usr/bin/ld: cannot find -lc等编译问题
- 解决跨域问题
- 使用Visual Studio编译器编写程序时不显示窗口或窗口一闪而逝的解决方法
- CodeForces334B
- LeetCode22.Generate Parentheses
- 蓝桥杯 最大最小公倍数
- java int与integer的区别
- 网络学习——unity中Mathf数学运算
- 关于java的反射,调用私有方法(有参数私有方法),私有属性
- Java编程思想-9