Kyl*_*ner 4 django ajax jquery django-sessions
我有一个 django 应用程序,我只能通过 AJAX 访问它。我的主要问题是我想获得一个与发出请求的特定浏览器实例配对的唯一 ID。
为了尝试这样做,我正在尝试访问session_keydjango 创建的None.
这是我在 Django 中创建 JSON 响应的方法:
def get(self, request, pk, format=None):
resp_obj = {}
...
resp_obj['csrftoken'] = csrftoken
# shouldn't need the next two lines, but request.session.session_key is None sometimes
if not request.session.exists(request.session.session_key):
request.session.create()
resp_obj['sessionid'] = request.session.session_key
return JSONResponse(resp_obj)
Run Code Online (Sandbox Code Playgroud)
当我使用 Postman 发出请求时,session_keyJSON 正文和 cookie 中都通过了请求,但是当我在浏览器中通过 jquery 发出请求时request.session.session_key,它是 None,这就是我添加这些行的原因:
if not request.session.exists(request.session.session_key):
request.session.create()
Run Code Online (Sandbox Code Playgroud)
但是当我这样做时,session_key每次都不一样。
这是我如何进行 AJAX 调用:
if not request.session.exists(request.session.session_key):
request.session.create()
Run Code Online (Sandbox Code Playgroud)
Django 文档说会话并不总是被创建:
默认情况下,Django 仅在会话被修改时保存到会话数据库——也就是说,如果它的任何字典值已被分配或删除
https://docs.djangoproject.com/en/1.9/topics/http/sessions/#when-sessions-are-saved
有没有办法强制session_key创建django,即使会话未修改,但在不应该更改的情况下也不会更改?或者有没有办法“修改会话”,使其像邮递员一样正确创建?
您的问题基本上来自您请求的跨域部分。我复制了您的示例,并尝试使用 访问主页localhost,然后也发送Ajax请求localhost,并保存会话密钥。
但是,当我将Ajax请求更改为开启127.0.0.1(并正确配置django-cors-headers)时,我会遇到与您所描述的完全相同的问题。每次请求都会更改会话密钥。(CSRF 令牌也是如此,但这是故意的,应该保持这种方式。)
在这里,您混合使用了CORS、第三方cookie和凭据,XMLHttpRequest从而使整个过程中断。
首先,让我们就文件的内容达成一致。
# views.py
from django.http import JsonResponse
from django.middleware import csrf
def ajax_call(request):
if not request.session.exists(request.session.session_key):
request.session.create()
# To debug the server side
print request.session.session_key
print csrf.get_token(request)
# To debug the client side
resp_obj = {}
resp_obj['sessionid'] = request.session.session_key
resp_obj['csrf'] = csrf.get_token(request)
return JsonResponse(resp_obj)
Run Code Online (Sandbox Code Playgroud)
包含在较大 HTML 页面中的 Javascript 代码:
# Javascript, client side
function call () {
$.ajax({
type: 'GET',
xhrFields: {
withCredentials: true
},
url: 'http://127.0.0.1/ajax_call/'
}).fail(function () {
console.log("Error");
}).done(function (response, textStatus, jqXHR) {
console.log("Success");
console.log(response.sessionid);
console.log(response.csrf);
})
}
Run Code Online (Sandbox Code Playgroud)
请注意,Ajax 请求是在 上发出的127.0.0.1,而不是在 上localhost,就像您的情况一样。(或者,您访问主 HTML 页面127.0.0.1并localhost在 Ajax 调用中使用,但想法是相同的。)
最后,允许CORS在settings.py通过添加适当的线INSTALLED_APPS和MIDDLEWARE(见的Django-CORS-头就安装更多信息)。为方便起见,您允许所有 URL,并ORIGIN通过添加以下行:
# settings.py
CORS_ORIGIN_ALLOW_ALL = True
CORS_URLS_REGEX = True
Run Code Online (Sandbox Code Playgroud)
如果您不了解它的作用,请不要在生产服务器上复制这些行!它可以打开一个服务器安全漏洞。
GET主页发送请求(假设它是网站的根,请求是GET http://localhost)在这里,会话是否在您的浏览器和 之间打开并不重要localhost,因为状态可能不会改变。
服务器发回包含 Javascript 代码的 HTML 页面。
您的浏览器会解释 Javascript 代码。它创建一个XMLHttpRequest,发送到地址处的服务器http://127.0.0.1/ajax_call。如果一个会话之前是用 开启的localhost,这里就不能使用了,因为域名不一样。无论如何,没关系,因为可以在回答此请求时创建会话。
服务器收到对 的请求127.0.0.1。如果您没有将此主机添加为允许的主机,settings.py并且您在生产模式下运行服务器,则会引发异常。故事的结局。如果您在DEBUG模式下运行,服务器会创建一个新会话,因为客户端没有发送任何会话。一切都按预期进行,创建了一个新的session_key和一个新CSRF token的,并在标头中发送到客户端。让我们更准确地看一下。
以下是浏览器发送的标头示例:
Host: localhost
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101 Firefox/52.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: http://localhost/
X-Requested-With: XMLHttpRequest
Cookie: csrftoken=XCadvu4MJjDMzCkOTTC276oJs9P0j989CBw6rnidz7cS34PoOt1VftqWMqd8BHMX; django_language=en
DNT: 1
Connection: keep-alive
Run Code Online (Sandbox Code Playgroud)
仔细看Cookie线。没有sessionid发送,因为没有会话打开。
如果一切顺利,服务器发回的标头看起来像这样。
Content-Length: 125
Content-Type: application/json
Date: Sun, 17 Sep 2017 14:21:23 GMT
Server: WSGIServer/0.1 Python/2.7.14rc1
Set-Cookie: csrftoken=XCadvu4MJjDMzCkOTTC276oJs9P0j989CBw6rnidz7cS34PoOt1VftqWMqd8BHMX; expires=Sun, 16-Sep-2018 14:21:22 GMT; Max-Age=31449600; Path=/
sessionid=l505q4y8pywe9t3q76204662a1225scx; expires=Sun, 01-Oct-2017 14:21:22 GMT; httponly; Max-Age=1209600; Path=/
Vary: Cookie
x-frame-options: SAMEORIGIN
Run Code Online (Sandbox Code Playgroud)
服务器创建了一个会话,并将所有需要的信息作为Set-Cookie标头发送给客户端。看起来挺好的 !
sessionid头中的 ,使服务器创建一个新会话,将新信息作为Set-Cookie头发送,并重新开始该过程。如果您查看计算机上存储的 cookie(使用 Firefox,右键单击页面 > View Page Info,然后选择Security标签,单击View Cookies),您可以看到一些 cookie localhost,但没有cookie 127.0.0.1。浏览器不会设置它从 Ajax 调用中获取的 cookie。原因太多了!我们将在解决方案部分列出所有这些。因为解决这个问题的最简单和最好的方法不是解锁它,而是正确使用它。
这是第一个、最好、最简单的推荐解决方案:使您的主域和 Ajax 调用域匹配。您将避免CORS头痛,避免打开潜在的安全漏洞,不处理第三方 cookie,并使您的开发环境与您的生产保持一致。
如果你真的想处理两个不同的域,问问自己是否需要它。如果没有,回到1。如果你真的需要它,让我们检查解决方案。
Preferences,转到Privacy选项卡。在该History部分中,选择Firefox will: Use custom settings for history,并检查是否Accept third-party cookies设置为Always。如果访问者禁用第三方 cookie,您的网站将被他破坏。(转到解决方案 1 的第一个充分理由。)其次,您必须告诉您的浏览器在收到 Ajax 调用的回复时不要忽略 Cookie 设置。将withCredentials设置添加到您的xhr就是解决方案。这是新的 Javascript 代码:
function call () {
$.ajax({
type: 'GET',
xhrFields: {
withCredentials: true
},
url: 'http://127.0.0.1/ajax_call/'
}). [...]
Run Code Online (Sandbox Code Playgroud)如果您现在尝试,您会看到浏览器仍然不满意。原因是
跨域请求被阻止:同源策略不允许读取位于“ http://127.0.0.1/ajax_call/ ”的远程资源。(原因:如果 CORS 标头“Access-Control-Allow-Origin”为“*”,则不支持凭据)。
这是一项安全功能。使用此配置,您可以再次打开已被CORS保护阻止的安全门闩。您允许任何网站发送经过身份验证的CORS请求。永远不要那样做。
使用CORS_ORIGIN_WHITELIST而不是CORS_ORIGIN_ALLOW_ALL修复此问题。
还没有完成。您的浏览器现在抱怨其他内容:
跨域请求被阻止:同源策略不允许读取位于http://127.0.0.1:8000/ajax_call/的远程资源。(原因:CORS 标头“Access-Control-Allow-Credentials”中的预期为“true”)。
同样,这是一项安全功能。CORS大多数情况下,通过请求发送凭据确实很糟糕。您必须在资源端手动允许它。您可以通过激活CORS_ALLOW_CREDENTIALS设置来实现。您的settings.py文件现在看起来像这样:
CORS_URLS_REGEX = r'.*'
CORS_ORIGIN_WHITELIST = (
'localhost',
'127.0.0.1',
)
CORS_ALLOW_CREDENTIALS = True
Run Code Online (Sandbox Code Playgroud)现在,您可以再试一次,看看它是否有效,然后再问自己是否真的需要在 Ajax 调用中使用不同的域。
| 归档时间: |
|
| 查看次数: |
2684 次 |
| 最近记录: |