#什么是跨域?
回顾一下url的组成:
浏览器遵循同源策略(scheme(协议),host(主机),path(路径)),这三者都相同则为同源,那么非同源会有以下限制:
- 不能读取和修改对方的 DOM
- 不读访问对方的 Cookie、IndexDB 和 LocalStorage
- 限制 XMLHttpRequest 请求。(后面的话题着重围绕这个)
当浏览器发送Ajax请求时,目标URL和当前的URL不同源,那么则会产生跨域的需求(跨域请求)
跨域请求一般都会被浏览器给拦截,那这个拦截是如何发生呢?
首先要知道的是,浏览器是多进程的,以 Chrome 为例,进程组成如下: 

详情:http://47.98.159.95/my_blog/http/014.html#%E4%BB%80%E4%B9%88%E6%98%AF%E8%B7%A8%E5%9F%9F
#跨域的几种解决方案
# JSONP
jsonp的原理:虽然XMLHttpRequest对象遵循同源策略,但script标签不一样,src属性可以填上目标地址,从而发送get请求,实现跨域请求并拿到响应,这也就是 JSONP 的原理.接下来我们封装一个jsonp
jsonp:
// 1. script 标签可以跨域// 2. script 标签 如果请求失败之后,如果跨域的话,没有具体的错误信息// 为什么?// 和安全有关function jsonp(req) {const {params,url} = reqconst callbackName = 'laizhenhang'const generateUrl = (url, params, callbackName) => {//localhost:9000/api/user?id=1&age=18&jsonpCallback=laizhenhanglet paramsStr = '?'for (const key in params) {paramsStr += `${key}=${params[key]}&`}paramsStr += `jsonpCallback=${callbackName}`const fullURL = url + paramsStrreturn fullURL}// 1. 创建 script 元素,将其 src 指向 要请求的url, 携带 callback的名字,让服务端返回该脚本,让前端执行const scriptEL = document.createElement('script')scriptEL.src = generateUrl(url, params, callbackName)return new Promise((resolve, reject) => {// 2. append script元素,让浏览器去发起script的get请求document.body.appendChild(scriptEL)window[callbackName] = (data) => {// 3. 执行回调之后,执行用户的回调或者让用户知道请求成功或失败resolve(data)// 4. 最后,收尾. 移除我们添加的 scrip 元素, 以及 删除 我们在 window 上添加的函数document.body.removeChild(scriptEL)delete window[callbackName]}// 超时的话,reject})}jsonp({url: 'http://localhost:9000/user',params: {id: 1,age: 18}}).then(data => {console.log(data)})// jsonp的兼容性// jsonp的安全性// jsonp的局限性
相应服务端的代码
const {json} = require('body-parser')const express = require('express')const app = express()const port = 9000app.get('/user', (req, res) => {const {age,id,jsonpCallback} = req.queryconsole.log(jsonpCallback)// 通过 传过来的参数,查数据库,返回数据库的结果// code// const result = db.query(`select age=${age},id=${id}`)res.send(`${jsonpCallback}('woshibendan')`)// laizhenhang()})app.listen(port, () => {console.log(`app is running at ${port}!`)})
JSONP 最大的优势在于兼容性好,IE 低版本不能使用 CORS 但可以使用 JSONP,缺点也很明显,请求方法单一,只支持 GET 请求。
JSONP 的安全性
- 因为jsonp只能发送get请求,而给请求的url都会在浏览器的输入url显示,那么传给服务端的参数就会被看到
- 防止callback参数恶意添加标签(如script),造成XSS漏洞
- 防止callback参数意外截断js代码,特殊字符单引号双引号,换行符均存在风险
- 防止跨域请求滥用,阻止非法站点恶意调用
# Nginx
Nginx 是一种高性能的反向代理服务器,可以用来轻松解决跨域问题。
正向代理帮助客户端访问客户端自己访问不到的服务器,然后将结果返回给客户端。
反向代理拿到客户端的请求,将请求转发给其他的服务器,主要的场景是维持服务器集群的负载均衡,换句话说,反向代理帮其它的服务器拿到请求,然后选择一个合适的服务器,将请求转交给它。
因此,两者的区别就很明显了,正向代理服务器是帮客户端做事情,而反向代理服务器是帮其它的服务器做事情。
好了,那 Nginx 是如何来解决跨域的呢?
比如说现在客户端的域名为client.com,服务器的域名为server.com,客户端向服务器发送 Ajax 请求,当然会跨域了,那这个时候让 Nginx 登场了,通过下面这个配置:
server {listen 80;server_name client.com;location /api {proxy_pass server.com;}}
Nginx 相当于起了一个跳板机,这个跳板机的域名也是client.com,让客户端首先访问 client.com/api,这当然没有跨域,然后 Nginx 服务器作为反向代理,将请求转发给server.com,当响应返回时又将响应给到客户端,这就完成整个跨域请求的过程。
