Skip to main content

说说同源策略和跨域

同源策略是什么?

如果两个 URL 的协议、端口和域名都完全一致的话,则这两个 URL 是同源的。

http://www.baidu.com/s
http://www.baidu.com:80/ssdasdsadad

同源策略怎么做?

只要在浏览器里打开页面,就默认遵守同源策略。

优点

保证用户的隐私安全和数据安全。

缺点

很多时候,前端需要访问另一个域名的后端接口,会被浏览器阻止其获取响应。

比如甲站点通过 AJAX 访问乙站点的 /money 查询余额接口,请求会发出,但是响应会被浏览器屏蔽。

怎么解决缺点

跨域就是突破浏览器同源策略限制,实现数据传递。

跨域的方法

  • CORS

  • JOSNP

    使用跨域手段。

  1. JSONP(前端体系课有完整且详细的介绍)

    a.甲站点利用 script 标签可以跨域的特性,向乙站点发送 get 请求。

    b.乙站点后端改造 JS 文件的内容,将数据传进回调函数。

    c.甲站点通过回调函数拿到乙站点的数据。

  2. CORS(前端体系课有完整且详细的介绍)

    a.对于简单请求,乙站点在响应头里添加 Access-Control-Allow-Origin: http://甲站点 即可。

    b.对于复杂请求,如 PATCH,乙站点需要:

    i.响应 OPTIONS 请求,在响应中添加如下的响应头:

    Access-Control-Allow-Origin: https://甲站点
    Access-Control-Allow-Methods: POST, GET, OPTIONS, PATCH
    Access-Control-Allow-Headers: Content-Type

    ii.响应 POST 请求,在响应中添加 Access-Control-Allow-Origin 头。

    c.如果需要附带身份信息,JS 中需要在 AJAX 里设置 xhr.withCredentials = true 。

  3. Nginx 代理 / Node.js 代理

    前端 ⇒ 后端 ⇒ 另一个域名的后端

详情参考 MDN CORS 文档

如何用跨域解决前端技术

跨域是什么

CORS

通知后端,后端只需在服务端的响应头里添加一句,允许特定网址访问即可

if (path === '/friends.json') {
response.statusCode = 200
response.setHeader('Content-Type', 'text/json;charset=utf-8')

//新增:允许特定网址(http://localhost:9999)访问 或者 *
response.setHeader('Access-Control-Allow-Origin', 'http://localhost:9999')

response.write(fs.readFileSync('./friends.json'))
response.end()
}

参考文章:https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Access_control_CORS

JSONP

是什么

JSONP 是通过 script 标签加载数据的方式去获取 JSON 数据当做 JS 代码来执行。

简单来说,通过 script 标签请求另一个网站 JS 文件,JS 文件会执行一个回调,回调中返回后会夹带服务器的数据

详细说明:客户端请求一个 JS 文件,JS 文件提前在页面上声明一个回调函数,回调函数名随机生成的,当发送服务器请求时,函数名通过 callback 接口传参的方式传给后台服务器,当服务器解析到参数(函数名)后,原始数据会上「包裹」这个函数名(服务端把 JSON 数据加载到函数里)之后发送给前端。前端就可以拿到服务端数据

为什么用 JSONP

当我们在进行跨域时,如果当前浏览器不支持 CORS,或者因为某些条件不支持 CORS,则会使用 JSONP 进行跨域

JSONP 优点

  • 兼容 IE
  • 可以跨域

JSONP 缺点

  • 由于 JSONP 是 script 标签,无法像 AJAX 读取状态码,只知道成功和失败
  • 由于 JSONP 是 script 标签,他只能发 get 请求,不支持 post 请求
  • JSONP 需要对应接口的后端的配合才能实现。

只能用 get 请求,只支持跨域 HTTP 请求这种情况,不能解决不同域的两个页面之间如何进行 JavaScript 调用的问题 JSONP 的优势在于支持老式浏览器,弊端也比较明显:需要客户端和服务端定制进行开发,服务端返回的数据不能是标准的 Json 数据,而是 callback 包裹的数据。

示例

客户端 frank.js

思路:

  1. 声明 random 随机 callback
  2. 创建 script 标签
  3. 全局中获取数据
  4. 删除 script 标签
function jsonp(url) {
return new Promise((resolve, reject) => {
const random = 'frnkJSONPCallbackName' + Math.random()

const script = document.createElement('script')

script.src = `${url}?callback=${random}`

document.body.appendChild(script)

window[random] = data => {
resolve(data)
}
script.error = () => {
reject()
}
script.onload = () => {
script.remove()
}
})
}

jsonp('http://localhost:8888/friends.js').then(res => console.log(res))

服务端 server.js

思路:

  1. 查看 referer 来源
  2. 设置相应头信息
  3. 数据库查找数据后 写入相应体 挂载到全局
  4. 返回相应
if (path === '/friends.js') {
console.log(request.headers['referer'])

if (request.headers['referer'].indexOf('http://localhost:9999/index.html') === 0) {
response.statusCode = 200

console.log(query.callback)

response.setHeader('Content-Type', 'text/javascript;charset=utf-8')

const string = `window['{{xxx}}']( {{data}} )`

const data = fs.readFileSync('./friends.json').toString()

const string2 = string.replace('{{data}}', data).replace('{{xxx}}', query.callback)

response.write(string2)

response.end()
} else {
response.statusCode = 404

response.end()
}
}

参考文章: https://zhuanlan.zhihu.com/p/22600501