什么是同源
- 在控制台输入window.origin或者location.origin可以得到当前源。源=协议+域名+端口号;如果两个url的协议、域名、端口号完全一致,那么这两个url是同源的。不同源的页面之间不准相互访问数据,这是浏览器为保护用户隐私设置的安全策略。
- 注意: • www.baidu.com 和 www.baidu.com/s同源,二者只有路径不… • 和 www.baidu.com 不同源,因为端口不同; • 和 baidu.com 不同源,因为协议不同; • 和 news.baidu.com 不同源,因为域名不同
• 代码举例: 令index.html,qq.js和friends.js的服务器为server.json,代码依次如下,url为http://127.0.0.1:8888
- index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>QQ空间</title>
</head>
<body>
你的QQ空间
</body>
<script src="qq.js"></script>
</html>
- qq.js
const request = new XMLHttpRequest()
request.open('GET','/friends.json')
request.onreadystatechange = () =>{
if(request.readyState === 4 && request.status === 200){
console.log(request.response)
}
}
request.send()
- friends.json
[ {"name":"彭于晏"}, {"name":"林俊杰"}]
- server.js
var http = require('http')
var fs = require('fs')
var url = require('url')
var port = process.argv[2]
if(!port){
console.log('请指定端口号好不啦?\nnode server.js 8888 这样不会吗?')
process.exit(1)
}
var server = http.createServer(function(request, response){
var parsedUrl = url.parse(request.url, true)
var pathWithQuery = request.url
var queryString = ''
if(pathWithQuery.indexOf('?') >= 0){ queryString = pathWithQuery.substring(pathWithQuery.indexOf('?')) }
var path = parsedUrl.pathname
var query = parsedUrl.query
var method = request.method
/******** 从这里开始看,上面不要看 ************/
console.log('有个傻子发请求过来啦!路径(带查询参数)为:' + pathWithQuery)
if(path === '/index.html'){
response.statusCode = 200
response.setHeader('Content-Type', 'text/html;charset=utf-8')
response.write(fs.readFileSync('./public/index.html'))
response.end()
} else if(path === '/qq.js'){
response.statusCode = 200
response.setHeader('Content-Type', 'text/javascript;charset=utf-8')
response.write(fs.readFileSync('./public/qq.js'))
response.end()
} else if(path === '/friends.json'){
response.statusCode = 200
response.setHeader('Content-Type', 'text/json;charset=utf-8')
response.write(fs.readFileSync('./public/friends.json'))
response.end()
}else {
response.statusCode = 404
response.setHeader('Content-Type', 'text/html;charset=utf-8')
response.write(`你输入的路径不存在对应的内容`)
response.end()
}
/******** 代码结束,下面不要看 ************/
})
server.listen(port)
console.log('监听 ' + port + ' 成功\n请用在空中转体720度然后用电饭煲打开 http://localhost:' + port)
- 此时运行http://127.0.0.1:8888/index.html时,浏览器会读取qq.js的GET请求,然后试图读取friends.json的数据。此时friends.json的数据被读取成功,因为qq.js和friends.json是同源的。可以打开控制台,通过检测Network中的Request Header Referer检验(如下图)
什么是跨域
- 浏览器规定如果JS运行在源A里,那么只能获取源A的数据,不能获取源B的数据,也就是不允许跨域。所以,跨域就是允许不同源的页面之间相互访问数据。
- 禁止跨域:假设上面举例的网站url为qq.com,有黑客网站fake.com图获取用户在网站qq.com的信息。黑客网站的服务器server.js、网站首页index.html和js文件fake.js代码依次如下
- server.js
var http = require('http')
var fs = require('fs')
var url = require('url')
var port = process.argv[2]
if(!port){
console.log('请指定端口号好不啦?\nnode server.js 8888 这样不会吗?')
process.exit(1)
}
var server = http.createServer(function(request, response){
var parsedUrl = url.parse(request.url, true)
var pathWithQuery = request.url
var queryString = ''
if(pathWithQuery.indexOf('?') >= 0){ queryString = pathWithQuery.substring(pathWithQuery.indexOf('?')) }
var path = parsedUrl.pathname
var query = parsedUrl.query
var method = request.method
/******** 从这里开始看,上面不要看 ************/
console.log('有个傻子发请求过来啦!路径(带查询参数)为:' + pathWithQuery)
if(path === '/index.html'){
response.statusCode = 200
response.setHeader('Content-Type', 'text/html;charset=utf-8')
response.write(fs.readFileSync('./public/index.html'))
response.end()
} else if(path === '/fake.js'){
response.statusCode = 200
response.setHeader('Content-Type', 'text/javascript;charset=utf-8')
response.write(fs.readFileSync('./public/fake.js'))
response.end()
} else if(path === '/'){
response.statusCode = 200
response.setHeader('Content-Type', 'text/javascript;charset=utf-8')
response.write('黑客网站首页')
response.end()
}else {
response.statusCode = 404
response.setHeader('Content-Type', 'text/html;charset=utf-8')
response.write(`你输入的路径不存在对应的内容`)
response.end()
}
/******** 代码结束,下面不要看 ************/
})
server.listen(port)
console.log('监听 ' + port + ' 成功\n请用在空中转体720度然后用电饭煲打开 http://localhost:' + port)
- index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>黑客网站</title>
</head>
<body>
我是黑客网站
</body>
<script src="fake.js"></script>
</html>
- fake.js
const request = new XMLHttpRequest()
request.open('GET','http://qq.com:8888/friends.json')
request.onreadystatechange = () =>{
if(request.readyState === 4 && request.status === 200){
console.log(request.response)
}
}
request.send()
- 此时登入黑客网站fake.com:9999/index.html时,浏览器会读取fake.js的GET请求,然后试图读取url为qq.com:8888/friends.jso…
- 此时浏览器控制台会打印出“从黑客网站发出的的XMLHttpRequest被阻拦;No Access-Control-Allow-Origin header”字样。
CORS跨域
- CORS的全称是跨域资源共享标准(Cross-Origin Sharing Standard),它允许在下列场景中使用跨域HTTP请求:
- 由XMLHttpRequest 或 Fetch发起的跨域HTTP请求
- Web字体(CSS中通过@font-face使用跨域字体资源),网站可以发布TrueType字体资源并只允许已授权网站进行跨域调用
- 使用drawImage将Images/video画面绘制到canvas 课程里所讲、和代码中的举例均只涉及第一种情况。 注意:IE 6 7 8 9都不支持CORS
- CORS跨域语法:Access-Control-Allow-Origin
- 举例:用CORS让fake.com可以访问qq.com的数据,只需在qq.com的服务器server.js文件中更新如下,在if(path === '/friends.json'){ }函数中增加响应头response.setHeader('Access-Control-Allow-Origin', 'fake.com:9999')即可通过黑客网站fake.com访问qq.com的friends.json。
else if(path === '/friends.json'){
response.statusCode = 200
response.setHeader('Content-Type', 'text/json;charset=utf-8')
response.setHeader('Access-Control-Allow-Origin','http://fake.com:9999')
response.write(fs.readFileSync('./public/friends.json'))
response.end()
}
JSONP跨域
- Wiki 定义:JSONP,或JSON-P(JSON with Padding),是一种历史悠久的JavaScript技术,通过加载
- 在跨域时,由于当前浏览器或起其他因素导致不支持CORS的情况下跨域的操作。JSONP在请求一个JS文件的时候,这个JS文件会执行一个回调,这个回调有需要跨域的数据。回调的名字可以随机生成,可以是一个随机数,随机数以callback的形式传给后台以后,后台会把这个函数再次返回并执行。
- JSONP的优点有:
- 兼容IE
- 可以跨域,即便请求的不是当前IP还是可以跨域
- JSONP的缺点有:
- 由于它是script标签,所以读不到AJAX精确的状态(无状态码,无响应头,只知道是成功还是失败)
- 由于它是script标签,所以它只能发送GET请求,不支持发送POST请求。
- 代码示例:试图通过JSONP实现黑客网站访问qq.com中friends.json的操作,首先在qq.com中新建一个js文件,名为friends.js
window.xxx = {{data}}
然后修改qq.com的服务器server.js
else if(path === '/friends.js'){
response.statusCode = 200
response.setHeader('Content-Type', 'text/javascript;charset=utf-8')
const string = fs.readFileSync('./public/friends.js').toString()
const data = fs.readFileSync('./public/friends.json').toString()
const string2 = string.replace('{{data}}', data)
response.write(string2)
response.end()
}
然后修改黑客网站的fake.js文件
const script = document.createElement('script') // 创建一个script标签
script.src = 'http://qq.com:8888/friends.js' // 访问qq.com新建的friends.js文件
script.onload = ()=>{ // 监听onload事件
console.log(window.xxx)
}
document.body.appendChild(script) // 把script标签放到页面里
之后再次访问黑客网站fake.com:9999/index.html时…