跨域的含义

同源策略以及其限制内容

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

同源策略限制内容有:

  • Cookie、LocalStorage、IndexedDB 等存储性内容
  • DOM 节点
  • AJAX 请求不能发送

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

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

常见跨域场景

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

特别说明两点:

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

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

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

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

实现跨域的方式之一:CORS

  • CORS 全称是跨域资源共享(Cross-Origin Resource Sharing),是一种 AJAX 跨域请求资源的方式,支持现代浏览器,IE支持10以上。

  • CORS与JSONP的使用目的相同,但是比JSONP更强大。JSONP只支持GET请求,CORS支持所有类型的HTTP请求。JSONP的优势在于支持老式浏览器,以及可以向不支持CORS的网站请求数据。

  • 浏览器会自动进行 CORS 通信,实现 CORS 通信的关键是后端。只要后端实现了 CORS,就实现了跨域。

  • 服务端设置 Access-Control-Allow-Origin 就可以开启 CORS。 该属性表示哪些域名可以访问资源,如果设置通配符(*)则表示所有网站都可以访问资源

    • response = HttpResponse('向非同源的网址返回其请求所对应的响应数据')
    • response["Access-Control-Allow-origin"] = "*"

前端发送请求类型

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

简单请求

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

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

  • GET
  • HEAD
  • POST

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

  • text/plain
  • multipart/form-data
  • application/x-www-form-urlencoded

请求中的任意 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.htmlhttp://localhost:4000/跨域请求,正如我们上面所说的,后端是实现 CORS 通信的关键。

应用django-cors-headers

drf后端+vue前端的项目

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

# vue前端域名
www.aliang.cn # drf后端域名
api.aliang.com

修改vue项目的配置信息

  • 通过浏览器访问前端vue项目,会出现nginx的欢迎页面,主要因为我们当前操作中已经有一个nginx监听了80端口,所以访问www.aliang.cn网址时,会自动被转发到127.0.0.1本机,因为没有网址默认端口是80端口,所以被nginx进行处理了当前请求,因此我们暂时先把nginx关闭。
# 查找nginx的进程
ps -ef|grep nginx
# 关闭进程
sudo kill -9 nginx进程号
  • 修改vue项目的配置文件config/index.js
    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,
  • 保存修改信息,并重启项目

  • 通过浏览器访问drf项目,会出现以下错误信息

    • You may need to add 'api.aliang.com' to ALLOWED_HOSTS
  • 可以通过设置drf项目中的配置文件,settings/dev.py的ALLOWED_HOSTS,设置允许访问

# 设置哪些客户端可以通过地址访问到后端
ALLOWED_HOSTS = [
'api.aliang.cn',
]
  • 现在,前端与后端分处不同的域名,我们需要为后端添加跨域访问的支持否则前端使用axios则无法请求到后端提供的api数据,故使用CORS来解决后端对跨域访问的支持。

使用django-cors-headers扩展

  • 安装
pip install django-cors-headers -i https://pypi.douban.com/simple/
  • drf后端项目配置文件中添加应用
INSTALLED_APPS = (
...
'corsheaders',
...
)
  • drf后端项目配置文件中设置中间件【必须写在第一个位置】
MIDDLEWARE = [
'corsheaders.middleware.CorsMiddleware',
...
]
  • drf后端项目配置文件中添加白名单
# CORS组的配置信息
CORS_ORIGIN_WHITELIST = (
'www.aliang.cn:8080',
)
CORS_ALLOW_CREDENTIALS = True # 允许ajax跨域请求时携带cookie

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

  • 前端使用 axios就可以访问到后端提供给的数据接口,但是如果要附带cookie信息,前端还要设置一下。

    • 需要注意的是,如果要发送Cookie,Access-Control-Allow-Origin就不能设为星号,必须指定明确的、与请求网页一致的域名。同时,Cookie依然遵循同源政策,只有用服务器域名设置的Cookie才会上传,其他域名的Cookie并不会上传,且(跨源)原网页代码中的document.cookie也无法读取服务器域名下的Cookie。
  • 前端引入axios插件并配置允许axios发送cookie信息[axios本身也不允许ajax发送cookie到后端]

npm i axios -S
  • 在main.js中引用 axios插件
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里面包的下载

使用cors完成跨域请求处理的更多相关文章

  1. flask之CORS跨域请求处理

    from flask import Flask from flask_cors import CORS#pip install Flask-CORS#跨域请求模块 app = Flask(__name ...

  2. Vuex、axios以及跨域请求处理

    一.Vuex 1.介绍 vuex是一个专门为Vue.js设计的集中式状态管理架构. 对于状态,我们把它理解为在data中需要共享给其他组件使用的部分数据. Vuex和单纯的全局对象有以下不同: 1. ...

  3. Nginx通过CORS实现跨域

    Nginx通过CORS实现跨域 2016-09-01 10:33 阅读 9.4k 评论 0 社区广播:运维派(Yunweipai.com)是国内最早成立的IT运维社区,欢迎大家投稿,让运维人不再孤寂的 ...

  4. System.Web.Http.Cors配置跨域访问的两种方式

    System.Web.Http.Cors配置跨域访问的两种方式 使用System.Web.Http.Cors配置跨域访问,众多大神已经发布了很多文章,我就不在详细描述了,作为小白我只说一下自己的使用心 ...

  5. HTML5安全:CORS(跨域资源共享)简介。。。ie67不要想了。。。

    来源:http://blog.csdn.net/hfahe/article/details/7730944 前言:像CORS对于现代前端这么重要的技术在国内基本上居然很少有人使用和提及,在百度或者Go ...

  6. Angular通过CORS实现跨域方案

    以前有一篇很老的文章网上转了很多,包括现在如果你百度"跨域"这个关键字,前几个推荐的都是"Javascript跨域总结与解决方案".看了一下感觉手段有点陈旧了, ...

  7. CORS(跨域资源共享)简介

    前言:像CORS对于现代前端这么重要的技术在国内基本上居然很少有人使用和提及,在百度或者Google上搜索CORS,搜到的中文文章基本都是另外一种卫星定位技术CORS的介绍,让我等前端同学情何以堪(对 ...

  8. 跨域解决方案一:使用CORS实现跨域

    跨站HTTP请求(Cross-site HTTP request)是指发起请求的资源所在域不同于请求指向的资源所在域的HTTP请求. 比如说,我在Web网站A(www.a.com)中通过<img ...

  9. HTML5安全:CORS(跨域资源共享)简介

    前言:像CORS对于现代前端这么重要的技术在国内基本上居然很少有人使用和提及,在百度或者Google上搜索CORS,搜到的中文文章基本都是另外一种卫星定位技术CORS的介绍,让我等前端同学情何以堪(对 ...

随机推荐

  1. day1:注释和变量

    1.注释的作用:对代码的解释,方便以后阅读代码 2.常用的快捷键:ctrl+q:notepad++的注释ctrl+/:pycharm的注释ctrl+c:复制ctrl+v:粘贴ctrl+z:撤销ctrl ...

  2. .NET Core微服务开发网关篇-ocelot

    通过上篇我们知道,网关是外部访问的统一入口,本文采用Ocelot作为Api网关. 环境要求: vs2019 .NetCore3.1 Ocelot16.0.1 创建一个产品服务Api站点(AAStore ...

  3. Java常用API(Random类)

    Java常用API(Random类) Random:是一个用于生成随机数的类 构造方法 public Random() :创建一个新的随机数生成器. 返回随机数的方法 public int nextI ...

  4. 如何用 Python 做自动化测试【进阶必看】

    一.Selenium 环境部署 1. window 环境部署 1.1 当前环境Win10 64 位系统:Python3.6.2(官方已经更新到了 3.6.4) 官方下载地址:https://www.p ...

  5. [Qt插件]-02创建应用程序插件(插件化开发的一种思路)

    本篇是学习Qt Creator快速入门,插件开发的笔记   分为两部分 创建插件 使用插件的应用程序(测试插件)   插件是被使用的应用程序加载使用的. 是使用插件的应用程序定义接口,插件按照接口来实 ...

  6. 转自fineui论坛:解决fineui框架开发中的Designer.aspx.cs丢失问题

    在开发的时候碰到个问题,本来好好的Edit.aspx  Edit.aspx.cs  Edit.Designer.aspx.cs编辑Edit.aspx然后保存,编译的时候 发现Edit.aspx.cs里 ...

  7. mybatis的<if>标签,<foreach>标签,<collection>标签,<association>标签以及useGeneratedKeys用法

    <if>标签 1.判断非空或不等于 <if test="assessTypes!= null and assessTypes!='' "> AND FIND ...

  8. SpringMVC集成Mybatis

    1.pom.xml中添加引入架包 <dependency> <groupId>mysql</groupId> <artifactId>mysql-con ...

  9. ThreadLocal 原理

    ThreadLocal是什么 ThreadLocal是一个本地线程副本变量工具类.主要用于将私有线程和该线程存放的副本对象做一个映射,各个线程之间的变量互不干扰,在高并发场景下,可以实现无状态的调用, ...

  10. python基础--深浅copy(重点)

    在此申明一下,博客参照了https://www.cnblogs.com/jin-xin/,自己做了部分的改动 深浅copy(重点) 先问问大家,什么是拷贝?拷贝是音译的词,其实他是从copy这个英文单 ...