• 使用cors完成跨域请求处理


    跨域的含义

    同源策略以及其限制内容

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

    img

    同源策略限制内容有:

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

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

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

    常见跨域场景

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

    img

    特别说明两点:

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

    第二:在跨域问题上,仅仅是通过“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里面包的下载

  • 相关阅读:
    JS下载文件并修改文件名
    分页查询优化
    SQL Server初体验
    LayUI样式优化
    LayUI框架应用常见问题
    SQL Server使用常见问题
    XXLJob框架入门介绍
    Java缓存框架整理
    osgearthvcpkg
    Git——commit的提交规范
  • 原文地址:https://www.cnblogs.com/fengting0913/p/13451489.html
Copyright © 2020-2023  润新知