当前位置: 首页>后端>正文

解决跨域问题(CORS、JSONP)

1 同源策略

1.1 同源

  • window.origin或者location.origin可以得到当前源
  • 源 = 协议 + 域名 + 端口号
  • 如果有两个url的 协议域名端口号完全一致,那么这两个url就是同源的
  • 在浏览器里打开页面则默认遵循同源策略
  • 前端测试时可以使用 postman 等工具,或安装在chrome里安装插件进行测试(不遵循同源策略,仅测试开发时可以使用)。

1.2 同源策略定义

  • 如果JS运行在源A里,那么就只能获取源A的数据
  • 不能获取源B的数据,即不允许跨域
  • 不同源的页面之间,不准互相访问数据

1.3 优缺点

优点

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

缺点

当前端需要访问另一个域名的后端接口时,会被浏览器阻止其获取相应。
eg:A 站点通过 AJAX 访问 B 站点的 /money 查询余额接口,请求可以发出,但响应会被浏览器屏蔽

1.4 疑问解答

  • a.qq.com和qq.com跨域:历史上出现过不同公司共用域名,因此这两者不一定是同一个网站,浏览器谨慎起见,认为这是不同的源
  • 不同端口算跨域:一个端口一个公司,因此不同端口可能属于不同公司,因此认为是不同的源
  • 同IP跨域:原因同上,IP是可以共用的
  • 跨域可以使用CSS、JS和图片等:同源策略限制的是数据访问,我们引用CSS、JS和图片时,其实并不知道其内容,只是在引用

1.5 跨域的解决方法

跨域问题根源:

  • 浏览器默认不同源之间不能互相访问数据

解决方法:

  • 方法1:CORS(不支持IE)
  • 方法2:JSONP(script标签存在缺陷)

2 CORS

  • 在server.js中进行设置,在需要跨域访问的文件设置中添加以下代码:
// 'http://xxxx:yyyy为设置允许访问这部分资源的网站地址及端口号
response.setHeader('Access-Control-Allow-Origin','http://xxxx:yyyy')
  • referer
    可以通过console.log(request.headers['referer'])读取想要获取此资源的网站

存在问题
无法兼容IE6、7、8、9浏览器,因此如果需要兼容IE浏览器,则需要使用JSONP

3 JSONP

3.1 简介

3.1.1 概念

在进行跨域操作时,由于部分浏览器不支持CORS,因此我们必须使用JSONP来执行跨域操作,我们需要请求一个JS文件,JS文件中会执行一个回调,回调中包含我们所需的数,回调的名称是随机生成的随机数,我们将这个随机数名称以callback的参数传给后台,后台会将函数返回给我们并执行。

3.1.2 优缺点

优点

  • 兼容IE
  • 可以跨域

缺点

  • 由于它是script标签,所以它无法读取到AJAX那么精确,接收不到状态码及响应头等
  • 由于它是script标签,因此它只能发get请求,不支持post

3.2 原理

通过依靠域内js获取域内json的内容,而后使用域外js调用域内获取到内容的js,从而获得域内json的内容

3.3 使用方法(以获取json文件内容为例)

  • 在json所在域内设置同域js文件用于获取json文件内容
  • js文件内使用占位符预留json内容放置空间
  • 在域内server.js文件中设置,使用域内js文件获取域内对应json文件内容
  • 域外则可以通过引用域内js文件,获取域内json文件内容

3.4 实例

要求:
1.sherry.com需要获取qq.com域内friend.json的数据
2.为回调函数设置随机函数名,以避免函数名被占用
3.通过referer设置仅限http://sherry.com:9999可以访问friend.json的内容

// qq.com内friend.json的内容
{
  "name": "sherry",
  "age": 18
}


// 在sherry.js定义随机函数,并打印数据,sherry.com可以通过window[random]获取数据
// window[random]是回调函数
const random =  Math.random()
window[random] = (data) => {
    console.log(data)
}


// qq.com内server.js设置
else if (path === '/friend.js') {
        // referer判定是否为http://sherry.com:9999,是进行操作,不是则返回404
        if (request.headers['referer'].indexOf('http://sherry.com:9999') === 0) {
            response.statusCode = 200
            response.setHeader('Content-Type', 'text/javascript;charset=utf-8')
            const string = `window[{{xxx}}] = {{data}}`
            const data = fs.readFileSync('./public/friend.json').toString()
            // 使用获取到的随机函数名query.functionName替换friend.js内的函数名占位符{{xxx}}
            string2 = string.replace('{{data}}', data).replace('{{xxx}}', query.callback)
            response.write(string2)
            response.end()
        } else {
            response.statusCode = 404
            response.end()
        }
    }


// sherry.com用于引用friend.js以获取friend.json内容的sherry.js设置
// 方法1:JS动态引用
const  script = document.createElement('script')
// 通过functionName=${random}传入随机函数名
const script = document.createElement('script')
script.src = `http://qq.com:8888/friend.js?functionName=${random}`
// 执行完毕后删除多余的script,保证页面整洁
script.onload = () => {
    script.remove()
}
document.body.appendChild(script)

// 方法2:HTML静态引用
// 在sherry.js对应的index.html页面设置
<script src="http://qq.com:8888/friend.js"></script>
// 在sherry.js中设置,用以监听内容是否获取成功
console.log(window.xxx)



代码封装jsonp()

function jsonp(url) {
    return new Promise((resolve, reject) => {
        const random = Math.random()
        window[random] = data => {
            resolve(data);
        };
        const script = document.createElement("script")
        script.src = `${url}?callback=${random}`
        script.onload = () => {
            script.remove();
        };
        script.onerror = () => {
            reject();
        };
        document.body.appendChild(script);
    });
}

// jsonp的使用
jsonp('http://qq.com:8888/friend.js')
    .then((data) => {
        console.log(data)
    })

3.4 缺陷及解决

任意网站都可以通过引用JS获取域内数据,可以通过referer检查来避免这个缺陷

// 检查是否是允许的网站,是则放行,不是则返回404
if (request.headers['referer'].indexOf('http://sherry.com:9999') === 0) {
            response.statusCode = 200
            response.setHeader('Content-Type', 'text/javascript;charset=utf-8')
            const string = fs.readFileSync('./public/friend.js').toString()
            const data = fs.readFileSync('./public/friend.json').toString()
            string2 = string.replace('{{data}}', data)
            response.write(string2)
            response.end()
        } else {
            response.statusCode = 404
            response.end()
        }

https://www.xamrdz.com/backend/3m81995545.html

相关文章: