跨域问题及解决(重要)
同源策略
同源策略(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', )
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义