9

谈谈前后端分离中的跨域问题

 3 years ago
source link: https://pylixm.top/posts/2020-10-13-cross-origin.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client
谈谈前后端分离中的跨域问题 - DeanWu
Beijing, China
欢迎关注微信公众号「码农吴先生」:专注python/golang/devops等技术的学习经验及资源分享!回复关键字:go或python 获取我收集的资料,也可回复关键字:小二,加我wx,一块聊技术,聊人生~

在前后端分离开发过程中常常出现下面这样的错误提示:

Access to XMLHttpRequest at 'http://127.0.0.1:8000/apis/users/login/' from origin 'http://127.0.0.1:8080' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
HelloWorld.vue?18db:50 err Error: Network Error
    at createError (createError.js?16d0:16)
    at XMLHttpRequest.handleError (xhr.js?ec6c:91)

看到关键字CORSAccess-Control-Allow-Origin 可以判断基本上就是跨域相关的错误了。

不了解的人常常一头雾水,本文咱们就来具体探讨下这种跨域问题,彻底搞懂它,解决它。

什么是跨域?造成跨域的原因?

跨域问题是由浏览器的同源策略引起的,在后端编程语言的Http Client调用中不会存在。同源策略中的同源是说站点的协议域名端口都需要相同。

cross_origin.png

跨域便是请求不同源的站点的一种行为操作。

为了更好的了解跨域,我们先来了解下同源策略。同源策略是一种安全策略,它只允许访问来自同一站点的资源。同源策略又分为两种:

  • DOM 同源策略:禁止对不同源页面DOM进行操作;
  • XMLHttpRequest同源策略:禁止使用XHR对象对不同源的服务地址发起HTTP请求;

DOM同源策略,常常发生在iframe的使用中,iframe 中如果嵌套了不同源的页面便会发生跨域。iframe跨域和XHR同源策略造成的跨域解决方法一样。

XMLHttpRequest同源策略便是引起文章开头跨域问题的主要原因。当浏览器请求后端不同源的数据时,会向后端发起一个XHR的HTTP请求,浏览器和后端服务沟通,若没有跨域相关配置,则触发XHR同源策略限制,抛出异常。

XMLHttpRequest(简称XHR)对象用于与服务器交互。通过 XMLHttpRequest 可以在不刷新页面的情况下请求特定 URL,获取数据。除了浏览器的地址栏,浏览器和后端交互(通常是javascripts控制)都是通过XHR对象,在浏览器的console中可以看到XHR的请求。

xhr.png

作为用户,同源策略是浏览器对我们上网行为的一种保护。作为开发者,在web开发中确实会用到夸域来获取资源的情况。如何解决呢?下面总结几种常用的跨域解决方法。

常用的解决方法

这里以vue作为前端、Django 作为后端举例说明。

  • 前端服务地址为:http://127.0.0.1:8080
  • 后端接口地址为:http://127.0.0.1:8000

详细完整代码可见:https://github.com/pylixm/django-cross-origin-demo

跨域资源共享(CORS)

CORS(Cross-origin resource sharing,跨域资源共享)是一个 W3C 标准,定义了在必须访问跨域资源时,浏览器与服务器应该如何沟通。它的核心思想,使用自定义的HTTP头部信息让浏览器和后端进行沟通,来决定是否允许跨域请求。

CORS 方式解决跨域,主要需要后端支持,主流浏览器均已支持。站点在访问跨域资源的时候,浏览器会自动的添加HTTP头信息,自动完成与后端的沟通,用户无感知。

下面以vue+django项目说下如何实现。例如在前端我们有个登录的POST请求,我们使用axios直接跨域请求后端接口。如下:

  handleClick(){
  this.$axios.post("http://127.0.0.1:8000/apis/users/login/", {
    username: this.username,
    password: this.password,
  }).then(res=>{
      console.log('res',res)
    }).catch(err=>{
      console.log('err',err)
    })
  },

前端服务端口为8080,后端服务为8000。两个服务的端口不一致,发生了跨域。

Access to XMLHttpRequest at 'http://127.0.0.1:8000/apis/users/login/' from origin 'http://127.0.0.1:8080' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
HelloWorld.vue?18db:50 err Error: Network Error
    at createError (createError.js?16d0:16)
    at XMLHttpRequest.handleError (xhr.js?ec6c:91)

Django 可通过第三方的跨域库django-cors-headers添加支持。我们pip install django-cors-headers 并增加如下配置:

INSTALLED_APPS = [
    ... 
    'corsheaders',
    'django_demo.apps.SiteCenterConfig',
]
MIDDLEWARE = [
    'corsheaders.middleware.CorsMiddleware',
    ...
]

# 跨域支持
CORS_ALLOWED_ORIGINS = ['http://127.0.0.1:8080']  # 授权进行跨站点 HTTP 请求的源列表

# 因为跨域之后需要传递sessionid 到浏览器cookie,所以添加如下配置。
CORS_ALLOW_CREDENTIALS = True  # 允许 Cookie 包含在跨站点 HTTP 请求中
SESSION_COOKIE_SAMESITE = None  # django 自己的安全策略

前端增加携带cookie的参数,不需要cookie时,可不用设置:

  handleClick(){
  this.$axios.post("http://127.0.0.1:8000/apis/users/login/", {
    username: this.username,
    password: this.password,
  }
  ,{
    withCredentials:true  // 携带和设置cookie 
  }
  ).then(res=>{
      console.log('res',res)
    }).catch(err=>{
      console.log('err',err)
    })
  },

重启服务,再次访问前端,成功登录。

使用代理解决

使用代理解决,vue框架自带了代理转发,在vue配置文件中增加如下配置解决:

    proxyTable: {
      // 这里就是代理了
      '/apis': {
        target: 'http://127.0.0.1:8000/apis/',   //设置你调用的接口域名和端口号 别忘了加http,就是后台服务地址
        changeOrigin: true,
        pathRewrite: {
          '^/apis': ''
        }
      }
    },

在生产环境中,还可以使用Nignx作为代理来解决跨域问题。

以上两种方式,便是前后端分离中最常用的跨域解决方案了,除了这两种方案,还有如下几种:

  • jsonp
  • location.hash 跨域
  • postMessage 跨域
  • window.name 跨域
  • document.domain 跨域

这些都不常用,文本暂不讨论。

至此,在前后端分离中,跨域问题及解决方案便讨论完了。跨域问题,不了解的朋友会一头雾水,了解之后解决便可信手拈来。针对 vue+django 架构中的两种解决方案,使用一种即可解决跨域。在使用CORS方案时,携带Cookie时,注意增加相关配置。其他后端框架大都有成熟的组件支持,与django配置参数类似,大家可触类旁通。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK