likes
comments
collection
share

解决跨域问题的5种办法

作者站长头像
站长
· 阅读数 12

前言

跨域问题一直是前后端交互过程中遇见频率最高的问题,本文带来五种解决跨域问题的办法,总有一种适合你。

跨域

什么是跨域?

  • 同源:两个资源的 协议、域名、端口都相同。否则就是跨域

为什么要有跨域?

  • 浏览器的安全限制,为了防止读取非同源的DOM、Cookie、LocalStorage、IndexDB;

跨域请求的过程?

  • 向非同源发起请求,请求资源跨域时,浏览器一般会先发起预检请求,检测资源是否支持跨域,再发起真实请求,请求类型为 preflight ,请求方法为OPTIONS 表示预检请求。

解决跨域的五种方式

  1. 把协议、域名、端口改成相同的;
  2. 在响应头添加 Access-Control-Allow-Origin 相关字段来允许跨域,利用这种解决思路具体的实现的方式有 nginx配置、后端手动修改字段CORS、后端中间件请求拦截、谷歌插件等方式;
  3. JSONP,利用了script标签不受浏览器同源策略的影响;
  4. 反向代理,利用跨域只对浏览器有效,在node中开启跨域代理,负责请求转发。Vue和React都有代码配置,适用于开发环境;
  5. CSP(content-Security-policy)设置白名单。

下面来展开聊其中的几个具体实现

Nginx配置解决跨域问题

在nginx服务器中配置,为响应头添加跨域字段

location ^~ /api/ {
    add_header 'Access-Control-Allow-Origin' $http_origin; # 任何域名都支持跨域
    add_header 'Access-Control-Allow-Credentials' 'true';  # 允许前端带上cookie
    add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS';
    add_header Access-Control-Allow-Headers '*';
    if ($request_method = 'OPTIONS') { # 如果是预检请求也要支持跨域
        add_header 'Access-Control-Allow-Credentials' 'true';
        add_header 'Access-Control-Allow-Origin' $http_orig  in;
        add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
        add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
        add_header 'Access-Control-Max-Age' 1728000;
        add_header 'Content-Type' 'text/plain; charset=utf-8';
        add_header 'Content-Length' 0;
        return 204;
    }
}

JSONP解决跨域问题

JSONP利用了script标签不受浏览器同源策略的影响,可以通过src属性,请求非同源的js脚本。除了script标签,img、link标签也支持跨域。但是img只能跨域图片(其实也可以获取图片二进制再使用JS解析也能拿到数据,但没必要怎么做)、link只能跨域css。

JSONP的4大缺陷:

  1. 只支持get请求
  2. 不安全
  3. 有缓存
  4. 传递信息有限制

JSONP伪代码

其实就是本地定义一个函数,用发起请求(发起的JS脚本请求)的方式调用自己 前端HTML的代码

<script>
  function abc(data) {
    console.log('拿到了Data数据:')
    console.log(data)
  }
</script>
<script src="./js/getdata.js?callback=abc"></script>

同级目录JS文件夹下的getdata.js 文件

abc({ name: 'ls', age: 30 })  //回调了HTML中的abc函数

实现JSONP

前端部分

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <script>
        let script = document.createElement('script')
        script.src = 'http://localhost:4433/api/test?callback=func'
        document.body.appendChild(script)
        function func(obj){
            console.log(obj);
        }
    </script>
</body>
</html>

Node后端部分

const http = require('http')
const url = require('url')
const server = http.createServer()
server.on('request',function(req,res){
    // 将api/test?callback=func 解析为 query和pathname urlobj是个对象还有一起其他内容
    let urlobj = url.parse(req.url,true)
    console.log(urlobj);
    if(urlobj.pathname === '/api/test'){
        // 返回了一个函数 func({name:'mileschen'})
        res.end(`${urlobj.query.callback}(${JSON.stringify({name:'mileschen'})})`)
    }else{
        res.end('404')
    }
})
server.listen(4433,function(){
    console.log('server start');
})

jQuery发起JSONP请求

jQuery是通过动态创建和移除script标签的方式来发起JSONP请求。在发起SONP请求的时候,动态向<header> 中append一个<script> 标签;在JSONP请求成功以后,动态从<header> 中移除刚才append进去的<script> 标签。

  // 发起JSONP的请求
  $.ajax({
    url:'http://www.baidu.com:81/api/jsonp?name=zs&age=20',
    // 代表我们要发起JSONP的数据请求
    dataType:'jsonp',
    jsonp:'callback',  //发送到服务器的参数名称,默认值为callback
    jsonpCallback:'abc',    //自定义函数名字 不指定会生成一个随机的名字
    success:function(res) {
      console.log(res)
    }
  })

CSP配置解决跨域问题

CSP的作用: 防XSS等攻击的利器。CSP 的实质就是白名单制度,开发者明确告诉客户端,哪些外部资源可以加载和执行,等同于提供白名单。它的实现和执行全部由浏览器完成,开发者只需提供配置。CSP 大大增强了网页的安全性。攻击者即使发现了漏洞,也没法注入脚本,除非还控制了一台列入了白名单的可信主机。开发者明确告诉客户端(制定比较严格的策略和规则),哪些外部资源是可以加载和执行的 ,即使攻击者发现漏洞,但是它是没办法注入脚本的

两种方式实现CSP:

  1. 可以在HTTP响应头添加CSP字段并配置策略
  2. 可以在HTML标签中添加 <meta http-equiv="Content-Security-Policy"content="default-src 'self'; img-src https://freepngimg.com/ " />

总结

本文详细阐述了跨域问题的原因和五种解决方案;

  1. 把协议、域名、端口改成相同的
  2. 在响应头添加 Access-Control-Allow-Origin 相关字段来允许跨域
  3. JSONP
  4. 反向代理
  5. CSP

并针对几个常用的跨域方案展开介绍


感谢小伙伴们的耐心观看,本文为笔者个人学习记录,如有谬误,还请告知,万分感谢!如果本文对你有所帮助,还请点个关注点个赞~,您的支持是笔者不断更新的动力!