跨域问题及解决(重要)

同源策略

同源策略(Same origin policy)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响。可以说Web是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现

请求的url地址,必须与浏览器上的url地址处于同域上,也就是域名,端口,协议相同.

比如:我在本地上的域名是127.0.0.1:8000,请求另外一个域名:127.0.0.1:8001一段数据

浏览器上就会报错,个就是同源策略的保护,如果浏览器对javascript没有同源策略的保护,那么一些重要的机密网站将会很危险。

 

CORS简介

CORS(Cross-Origin Resource Sharing):跨域资源共享,允许不同的域来我的服务器拿数据。

CORS是一种允许当前域(domain)的资源(比如html/js/web service)被其他域(domain)的脚本请求访问的机制,通常由于同域安全策略(the same-origin security policy)浏览器会禁止这种跨域请求。

CORS请求分成两类

简单请求(simple request)和 非简单请求(not-so-simple request)

简单请求

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

复制代码
(1) 请求方法是以下三种方法之一:
    HEAD
    GET
    POST
(2)HTTP的头信息不超出以下几种字段:
    Accept
    Accept-Language
    Content-Language
    Last-Event-ID
Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain
复制代码

非简单请求

如果发送post请求,数据格式是json---》非简单请求,
非简单请求发两次,一次OPTIONS请求,一次真正的请求

 

CORS基本流程

浏览器将CORS请求分成两类:简单请求(simple request)非简单请求(not-so-simple request)。
浏览器发出CORS简单请求只需要在头信息之中增加一个Origin字段。
浏览器发出CORS非简单请求会在正式通信之前,增加一次HTTP查询请求,称为”预检”请求(preflight)。浏览器先询问服务器,当前网页所在的域名是否在服务器的许可名单之中,以及可以使用哪些HTTP动词和头信息字段。只有得到肯定答复,浏览器才会发出正式的XMLHttpRequest请求,否则就报错。

 

支持跨域,简单请求

服务器设置响应头:Access-Control-Allow-Origin = ‘域名’ 或 ‘*’

 

支持跨域,复杂请求

由于复杂请求时,首先会发送“预检”请求,如果“预检”成功,则发送真实数据。

  • “预检”请求时,允许请求方式则需服务器设置响应头:Access-Control-Request-Method
  • “预检”请求时,允许请求头则需服务器设置响应头:Access-Control-Request-Headers

 

跨域问题及解决

我们开启两个服务端,域名不同,通过端口2来访问端口1

复制代码
端口1:http://127.0.0.1:8000/user/test/

端口2:http://127.0.0.1:8080/cors/


上面2者域名不同,我们通过端口2来访问端口1
# 端口2:
<body>
<button onclick="test()">点击</button>

<script>
   function test(){
       $.ajax({
           url:'http://127.0.0.1:8000/user/test/',
           type:'get',
           data:'',
           success:function(ll){
               console.log(ll)
               alert('ok')
           }
       })
   }
</script>
</body>

# 端口2通过ajax请求来访问端口1
复制代码

结果如下:

如上面所示,端口1成功发送了请求,并且端口2接收到请求并响应,

但是响应回来的时候浏览器的同源策略阻止了端口1的访问,

那么,我们该如何解决这个问题呢?

简单请求的解决方式

如果是对方发起的是简单请求

我们只要在端口1的响应头里设置允许端口2域名的跨站资源请求即可,如下:

复制代码
# 端口1修改前:
def test(request):
    print('我运行了')
    res = HttpResponse('测试跨域资源共享')
    return res


# 端口1修改后:
def test(request):
    print('我运行了')
    res = HttpResponse('测试跨域资源共享')
    # 在端口1的请求头内将该参数设置为允许8080端口访问
    res['Access-Control-Allow-Origin'] = 'http://127.0.0.1:8080'
    return res

# 只要端口1的服务端设置了允许跨域资源访问,
# 被允许的端口就能访问到端口1服务端的资源
复制代码

 

 非简单请求解决方式

上面我们解决了简单请求跨域资源被禁止的情况,

但是当我们发起非简单请求时,还是会出现跨域资源被禁止的情况,如下:

复制代码
端口1:http://127.0.0.1:8000/user/test/

端口2:http://127.0.0.1:8080/cors/


# 端口2通过ajax向端口1发起post请求,携带Json格式数据
# 所以是非简单请求
<script>
   function test(){
       $.ajax({
           url:'http://127.0.0.1:8000/user/test/',
           type:'post',
           contentType:'application/json',
           data:JSON.stringify({'yessir':'nb'}),
           success:function(res){
               console.log(res)
               alert('ok')
           }
       })
   }
</script>


# 端口1服务端已经添加了允许跨域访问的响应头
def test(request):
    print('我运行了')
    print('请求方式为:',request.method)
    res = HttpResponse('测试跨域资源共享')
    # 在端口1的请求头内将该参数设置为允许8080端口访问
    res['Access-Control-Allow-Origin'] = 'http://127.0.0.1:8080'
    return res


# 我们发现当发起非简单请求的时候,
# 虽然我们在端口1设置了请求头,
# 还是会触发浏览器的同源策略的禁止跨域访问资源的情况
复制代码

 

我们端口2发起的请求方式为POST请求,但是端口1显示的却是OPTIONS,

其实当发起非简单请求的时候,非简单请求会发起2次请求,一次是options预检请求,

另一次才是真正的请求,所以我们只要在端口1服务端的响应头内加上该参数,

就可以访问到端口1的服务端资源了,具体如下:

复制代码
# 端口1修改前:
def test(request):
    print('请求方式为:',request.method)
    print('我运行了')
    res = HttpResponse('测试跨域资源共享')
    # 在端口1的请求头内将该参数设置为允许8080端口访问
    res['Access-Control-Allow-Origin'] = 'http://127.0.0.1:8080'
    return res


# 端口1修改后:
def test(request):
    print('请求方式为:',request.method)
    print('我运行了')
    res = HttpResponse('测试跨域资源共享')
    if request.method == 'OPTIONS':
        res['Access-Control-Allow-Headers'] = 'Content-Type'  
# 允许请求头里有一个Content-Type参数
# 在端口1的请求头内将该参数设置为允许8080端口访问 res['Access-Control-Allow-Origin'] = 'http://127.0.0.1:8080' return res # 在端口1的响应头内添加该参数,即可解决浏览器同源策略禁止跨域访问资源问题

复制代码

自定义中间件中解决CORS(重点)

但是如果我们需要跨域访问很多的接口,那我们就需要给每个接口的响应头都添加上这些参数,

这太过繁琐了,所以我们就可以在django中间件中重写process_response方法,

允许给响应头加上这些参数,这样我们就不需要为每个接口都配置上这些参数了

如下:在小luffyapi的utils包下,创建midware.py

复制代码
# utils/midware.py

from django.utils.deprecation import MiddlewareMixin
class MyMiddle(MiddlewareMixin): def process_response(self, request, response): response['Access-Control-Allow-Origin'] = '*' if request.method == "OPTIONS": # 可以加*(请求头里允许添加任意的参数) 表示允许在请求头里添加Content-Type和authorization参数 response["Access-Control-Allow-Headers"] = "Content-Type" response["Access-Control-Allow-Headers"] = "authorization" return response

复制代码

在配置中注册上自定义中间件

复制代码
# settings/div.py

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    # 'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    # 注册自定义中间件
    'luffyapi.utils.midware.CorsMiddleware'
]
复制代码

第三方插件解决CORS(重点)

安装django-cors-headers

第三方插件django-cors-headers帮我们解决了CORS同源策略问题,

我们可以使用第三方插件,这样我们就不需要自己写中间件

pip install django-cors-headers

在settings/div.py中配置

第三方插件其实就是一个应用,需要做如下3件事:

1.INSTALLED_APPS 中注册一下,

2.在 MIDDLEWARE 中间件中添加插件的中间件

3.在配置内配置插件信息

复制代码
# settings/dev.py


# 1.把corsheaders注册到apps中
INSTALLED_APPS = [
    ...
    # 注册django-cors-headers
    'corsheaders',
    ...
]


# 2.在中间件添加corsheaders中间件
MIDDLEWARE = [
    ...
    'corsheaders.middleware.CorsMiddleware',
    ...
]


# 3.添加corsheaders配置信息
CORS_ORIGIN_ALLOW_ALL = True
CORS_ALLOW_METHODS = (
    'DELETE',
    'GET',
    'OPTIONS',
    'PATCH',
    'POST',
    'PUT',
    'VIEW',
)
CORS_ALLOW_HEADERS = (
    'authorization',
    'content-type',
    'XMLHttpRequest',
    'X_FILENAME',
    'accept-encoding',
    'dnt',
    'origin',
    'user-agent',
    'x-csrftoken',
    'x-requested-with',
    'Pragma',
)
复制代码

 

posted @   _yessir  阅读(148)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
点击右上角即可分享
微信分享提示