cors跨域

25 篇文章 1 订阅
14 篇文章 0 订阅

一、问题

1.前端用ajax get访问接口,报错 No 'Access-Control-Allow-Origin' header is present on the requested resource.

二、原因

javascript处于安全考虑,不允许跨域访问,CORS导致的。

三、CORS

3.1、什么是CORS?

CORS(跨域资源共享,Cross-Origin Resource Sharing)是一种跨域访问的机制,可以让Ajax实现跨域访问。是一种基于HTTP头的机制,该机制通过允许服务器标示除了它自己以外的其他源,使得浏览器允许这些域加载自己的资源。
  • 服务器:标识哪些源可以访问自己的资源
  • HTTP头:Access-Control-Allow-Origin: *     # 星号允许所有的源访问
                        Origin: https://bilibili.com             # 表明该请求来自哪个源
  • 浏览器:必须实现CORS相关的规范;是否载入都有浏览器控制
在服务器的response header中,加入"Access-Control-Allow-Origin:*" 即可以支持CORS,apache/nginx等怎么配置,见参考文档。
  
举个例子:
    * API部署在DomainA上;
    * Ajax文件部署在DomainB上,Ajax文件会向API发送请求,返回数据;
    * 用户通过DomainC访问DomainB的Ajax文件,请求数据
以上过程就发生了跨域访问。如果直接使用Ajax来请求就会失败,就像Chrome提示的:No 'Access-Control-Allow-Origin' header is present on the requested resource.

3.2、CORS的工作机制

先打开百度首页,然后在浏览器console下直接使用fetch打开淘宝的首页:
​
fetch('https://www.taobao.com')
浏览器清楚这是一个跨域请求,用CORS机制的,所以浏览器会检查返回内容的头部,是否包含了 access control allow origin这个标头
切换到Network查看接口,可知这个例子中,访问淘宝的响应头中没有 access control allow origin,也就是不允许任何跨域的请求,因此,浏览器拒绝载入该内容。
Network中有一个请求:
同样在百度首页载入淘宝的页面,但这次请求加入定制的标头,并且内容改成了XML:
const xhr = new XMLHttpRequest();
xhr.open("GET", "https://www.taobao.com");
xhr.setRequestHeader("X-PINGOTHER", "pingpong");
xhr.setRequestHeader("Content-Type", "apptication/xml");
xhr.send("<person><name>Arun</name></person>");
Network中有两个请求:
得到了同样的CORS错误,在预检阶段失败了。
  
总结:
    对于 简单请求,浏览器会直接请求服务端,然后检查返回内容头部的 access control allow origin,如果允许当前域,则正常的载入并展示返回内容,否则拒绝载入。
    对于 非简单请求,浏览器会发出一个option方法,来询问服务器端, 是否允许来自当前域的请求访问该资源,服务器会返回允许访问的域HTTP方法以及允许的标头header,浏览器会进行判断后,决定是否发出真正的请求,这个询问的过程称为预检

3.3、简单请求与非简单请求的概念

设计后端请求时,将占用服务器过多资源的请求,通过预检可以降低服务器的负载,应该设计成非简单请求的服务。而轻量服务,则使用简单请求,提升响应速度。
# 简单请求请求
fetch('https://www.taobao.com')

# 预检请求,因为改动了请求头,不满足CORS简单请求的第二条要求
fetch('https://www.taobao.com', {
    headers: {
        a: 1,
    },
});

# POST请求,简单请求
fetch('https://www.taobao.com', {
    method: 'POST',
    body: JSON.stringify({a: 1, b: 2}),
});

# 预检请求,因为改动了请求头content-type,不满足CORS简单请求的第三条要求
fetch('https://www.taobao.com', {
    method: 'POST',
    headers: {
        'content-type': 'application/json',
    },
    body: JSON.stringify({a: 1, b: 2}),
});
​

3.4、为什么需要CORS?

  1. 保护服务器, 控制哪些域有权访问资源,有助于减轻服务器的负载与风险;
  2. 保护隐私,CORS限制了来自不同域的敏感数据的访问。例如,一个网站不能在没有用户授权的情况下,从其他域获取用户的个人信息;
  3. 安全考虑, 防止盗取用户数据或进行 CSRF攻击;
  4. 因为有实际的跨域请求需求,所以也不能完全禁止跨域请求;

四、如何解决Ajax跨域访问问题?

解决跨域问题有以下几种方法:
  1. 同源策略(增加反向代理)
  2. 使用jsonp
  3. 使用django-cors-headers
  4. 修改response header中,加入"Access-Control-Allow-Origin:*"
  5. nginx层配置“Access-Control-Allow-Origin *
使用jsonp方法,需让服务端返回jsonp格式的response,如:Returning JSON/JSONP from a Django view with a little decorator help (Example)
使用CORS:这个用起来比较方便,现在大多数浏览器都支持了,且我web服务器完全开放给别人调用,所以比较推荐CORS。

4.1、使用JSONP

使用Ajax获取json数据时,存在跨域的限制。不过,在Web页面上调用js的script脚本文件时却不受跨域的影响,JSONP就是利用这个来实现跨域的传输。因此,我们需要将Ajax调用中的dataType从JSON改为JSONP(相应的API也需要支持JSONP)格式。
 
JSONP只能用于GET请求。

4.2、使用django-cors-headers

pip install django-cors-headers
修改setting.py:
​INSTALLED_APPS = [
     'corsheaders' # 尽量放在前面
]

MIDDLEWARE = [
    'corsheaders.middleware.CorsMiddleware'  # 需要添加在CommonMiddleware中间件前面(必须)
]

# 添加白名单:设置可以任意访问,默认为False
CORS_ORIGIN_ALLOW_ALL =True

# 也可以通过白名单列表配置允许跨域访问的地址
CORS_ORIGIN_WHITELIST = (
    'localhost:3306',
    'http://localhost:8080'
)

# CORS_ALLOW_CREDENTIALS允许跨域时携带Cookie,默认为False
CORS_ALLOW_CREDENTIALS = True
​

4.3、在服务器的response header中,加入“Access-Control-Allow-Origin: *”

修改视图函数view.py,允许其他域通过Ajax请求数据:
def myview(_request):
  response = HttpResponse(json.dumps({"key": "value", "key2": "value"}))
  response["Access-Control-Allow-Origin"] = "*"
  response["Access-Control-Allow-Methods"] = "POST, GET, OPTIONS"
  response["Access-Control-Max-Age"] = "1000"
  response["Access-Control-Allow-Headers"] = "*"
  return response

4.4、nginx配置

location / {
    # 跨域请求
    add_header Access-Control-Allow-Origin '*';
    add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS';
    add_header Access-Control-Allow-Headers '*';

    # 发起预检请求,从而获知服务器是否允许该跨域请求
    if ($request_method = 'OPTIONS') {
        return 204;
    }
}
总结一下预检请求通过的条件
  1. OPTIONS 方法获取的Response header 字段中,有Access-Control-* 的字段。表示服务器告知浏览器服务端支持跨域访问的方法和header 字段等。
  2.  复杂请求中的request method、request header、Origin等满足第1点中服务端告知给客户端的服务器端接受的条件。
  3. 当前两点都满足的时候,浏览器会才会发起跨域请求,否则浏览器直接拦截跨域请求,该请求不会走到服务器端。
  
其他补充
  • Access-Control-Max-Age 可以指定将预检请求的结果缓存多长时间,在这个时间范围内就不用再重复发起预检请求了
  • Access-Control-Allow-Credentials true 是否允许在跨域请求中传递cookie ,默认false
  • Access-Control-Allow-Origin 要根据实际允许跨域的origin填写,* 表示允许所有origin 跨域访问

五、参考

Python的Django应用程序解决AJAX跨域访问问题的方法_python_脚本之家 (jb51.net)

(5条消息) Django解决AJAX跨域访问问题的方法_kan2016的博客-CSDN博客_ajax访问django找不到接口

掌握CORS跨域请求,看这个视频就够了【渡一教育】_哔哩哔哩_bilibili

彻底搞懂CORS(跨域资源共享)相关内容 什么是CORS 为什么需要CORS 如何解决CORS问题 简单请求 预检_哔哩哔哩_bilibili

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值