<返回更多

使用cors完成跨域请求处理

2020-08-10    
加入收藏

跨域的含义

同源策略以及其限制内容

同源策略是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,浏览器很容易受到XSS、CSFR等攻击。所谓 同源是指"协议+域名+端口" 三者相同,即便两个不同的域名指向同一个ip地址,也非同源。

使用cors完成跨域请求处理

 

同源策略限制内容有:

但是有三个标签允许跨域加载资源:

<img src="...">
<link href="...">
<scripts src="...">

常见跨域场景

当协议、子域名、主域名、端口号中任意一个不相同时,都算作不同域。不同域之间相互请求资源,就算作“跨域”。常见跨域场景如下图所示:

使用cors完成跨域请求处理

 

特别说明两点:

第一:如果是协议和端口造成的跨域问题“前端”是处理不了的。

第二:在跨域问题上,仅仅是通过“URL的首部”来识别而不会根据域名对应的IP地址是否相同来判断。“URL的首部”可以理解为“协议, 域名和端口必须匹配”。

这里你或许有个疑问: 请求跨域了,那么请求到底发出去没有?

跨域并不是请求发不出去,请求能发出去,服务端能收到请求并正常返回响应,只是响应被浏览器拦截了。你可能会疑问明明通过表单的方式可以发起跨域请求,为什么 Ajax 就不行?因为归根结底,跨域是为了阻止用户读取到另一个域名下的内容,Ajax 可以获取响应,浏览器认为这不安全,所以拦截了响应。但是表单并不会获取新的内容,所以可以发起跨域请求。同时也说明了 跨域并不能完全阻止 CSRF ,因为请求毕竟是发出去了。

实现跨域的方式之一:CORS

前端发送请求类型

虽然设置 CORS 和前端没什么关系,但是通过这种方式解决跨域问题的话,会在发送请求时出现两种情况,分别为 简单请求 和 复杂请求 。

简单请求

只要同时满足以下两大条件,就属于简单请求

条件1:使用下列方法之一:

条件2:Content-Type 的值仅限于下列三者之一:

请求中的任意 XMLHttpRequestUpload 对象均没有注册任何事件监听器; XMLHttpRequestUpload 对象可以使用 XMLHttpRequest.upload 属性访问。

复杂请求

不符合以上条件的请求就是复杂请求。

复杂请求的CORS请求,会在正式通信之前,增加一次HTTP查询请求,称为"预检"请求,该请求是 option 方法的,通过该请求来知道服务端是否允许跨域请求。

我们用 PUT 向后台请求时,属于复杂请求,后台需做如下配置:

// 允许哪个方法访问我
res.setHeader('Access-Control-Allow-Methods', 'PUT')
// 预检的存活时间
res.setHeader('Access-Control-Max-Age', 6)
// OPTIONS请求不做任何处理
if (req.method === 'OPTIONS') {
  res.end() 
}
// 定义后台返回的内容
app.put('/getData', function(req, res) {
  console.log(req.headers)
  res.end('****')
})

接下来我们看下一个完整复杂请求的例子,并且介绍下CORS请求相关的字段

// index.html
let xhr = new XMLHttpRequest()
document.cookie = 'name=xiamen' // cookie不能跨域
xhr.withCredentials = true // 前端设置是否带cookie
xhr.open('PUT', 'http://localhost:4000/getData', true)
xhr.setRequestHeader('name', 'xiamen')
xhr.onreadystatechange = function() {
  if (xhr.readyState === 4) {
    if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
      console.log(xhr.response)
      //得到响应头,后台需设置Access-Control-Expose-Headers
      console.log(xhr.getResponseHeader('name'))
    }
  }
}
xhr.send()
//server1.js
let express = require('express');
let app = express();
app.use(express.static(__dirname));
app.listen(3000);
//server2.js
let express = require('express')
let app = express()
let whitList = ['http://localhost:3000'] //设置白名单
app.use(function(req, res, next) {
  let origin = req.headers.origin
  if (whitList.includes(origin)) {
    // 设置哪个源可以访问我
    res.setHeader('Access-Control-Allow-Origin', origin)
    // 允许携带哪个头访问我
    res.setHeader('Access-Control-Allow-Headers', 'name')
    // 允许哪个方法访问我
    res.setHeader('Access-Control-Allow-Methods', 'PUT')
    // 允许携带cookie
    res.setHeader('Access-Control-Allow-Credentials', true)
    // 预检的存活时间
    res.setHeader('Access-Control-Max-Age', 6)
    // 允许返回的头
    res.setHeader('Access-Control-Expose-Headers', 'name')
    if (req.method === 'OPTIONS') {
      res.end() // OPTIONS请求不做任何处理
    }
  }
  next()
})
app.put('/getData', function(req, res) {
  console.log(req.headers)
  res.setHeader('name', 'jw') //返回一个响应头,后台需设置
  res.end('****')
})
app.get('/getData', function(req, res) {
  console.log(req.headers)
  res.end('****')
})
app.use(express.static(__dirname))
app.listen(4000)

上述代码由 http://localhost:3000/index.html 向 http://localhost:4000/ 跨域请求,正如我们上面所说的,后端是实现 CORS 通信的关键。

应用django-cors-headers

drf后端+vue前端的项目

在做以drf框架作为后端支撑以及以vue前端框架作为页面展示的前后端分离的web项目中,前端会向后端请求数据,也会涉及到非同源跨域的问题,以下操作将做详细处理

# vue前端域名
www.aliang.cn

# drf后端域名
api.aliang.com

修改vue项目的配置信息

# 查找nginx的进程
ps -ef|grep nginx
# 关闭进程
sudo kill -9 nginx进程号
host: 'www.aliang.cn', // can be overwritten by process.env.HOST
    port: 8080, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined
    autoOpenBrowser: true,
# 设置哪些客户端可以通过地址访问到后端
ALLOWED_HOSTS = [
    'api.aliang.cn',
]

使用django-cors-headers扩展

pip install django-cors-headers -i https://pypi.douban.com/simple/
INSTALLED_APPS = (
    ...
    'corsheaders',
    ...
)
MIDDLEWARE = [
    'corsheaders.middleware.CorsMiddleware',
    ...
]
# CORS组的配置信息
CORS_ORIGIN_WHITELIST = (
    'www.aliang.cn:8080',
)
CORS_ALLOW_CREDENTIALS = True  # 允许ajax跨域请求时携带cookie

完成了上面的步骤,我们就可以通过后端提供数据给前端使用ajax访问了。

npm i axios -S
import axios from 'axios'; // 从node_modules目录中导入包
// 允许ajax发送请求时附带cookie
axios.defaults.withCredentials = true;

Vue.prototype.$axios = axios; // 把对象挂载vue中

如果你拷贝前端vue-cli项目到咱们指定目录下,如果运行起来有问题,一些不知名的错误,那么就删除node_modules文件件,然后在项目目录下执行 npm install 这个指令,重新按照package.json文件夹中的包进行node_modules里面包的下载

原文作者:阿良

原文链接:https://www.cnblogs.com/fengting0913/p/13451489.html

声明:本站部分内容来自互联网,如有版权侵犯或其他问题请与我们联系,我们将立即删除或处理。
▍相关推荐
更多资讯 >>>