Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/315.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/django/24.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python Django:跨多个域维护会话_Python_Django_Django Authentication_Django Sessions - Fatal编程技术网

Python Django:跨多个域维护会话

Python Django:跨多个域维护会话,python,django,django-authentication,django-sessions,Python,Django,Django Authentication,Django Sessions,我有两个/多个域,比如,foo.com和

我有两个/多个域,比如,
foo.com
,它们都有相同的后端,这意味着两个域都将未来的请求重定向到其他地方托管的相同“web实例”


当前行为 如果用户登录
foo.com
,他/她还需要登录
bar.com
,以访问任何端点/URL,如
bar.com/some/URL/end-point/


如果我有一个具有公共模式的域,可能会做一些事情。不幸的是,我没有。

问题

如何跨多个域维护用户会话?

我认为您无法跨完全不同的域进行单点登录。但是,如果两个域都指向同一个OAuth提供程序,您可以使用它吗?然后实现一个OAuth提供程序,该提供程序为任一域生成相同的访问令牌。不过,我不知道这需要付出多大的努力


从安全角度来看,这本身就是一种风险,任何解决方案都可能导致一个域从另一个域读取cookie。所以很明显,这不正常

现在,在大多数情况下,您希望共享的唯一内容是令牌或会话id。因此,您可以用不同的方式处理此问题

授权的重新定向 假设您的令牌是使用
example.com/auth
生成的。此url可以返回cookie中的令牌以及json响应。然后,您还可以将此url返回301到
example.org/preauth?token=XXX
。然后,此url将使用令牌设置cookie

所以基本上,在这种情况下,您可以在服务器端本身处理整个方法

使用像素标签 在本例中,您要做的是创建一个像素标记url。在
example.com/auth

您将使用javascript在页面上动态地向其他域添加一个图像源标记

<img src='http://example.org/cookiepixel?token=yyy' /> 

这将返回cookie,cookie将在
example.org
而不是
example.com


在这种方法中,您依赖客户端代码来确保跨域身份验证的发生。

这是一个有趣的问题。应该有很多方法,我想到的第一件事是使用
iframe
。下面的示例是用
Django 2.2
测试的

settings.py
中,将您的
sessionid
公开给javascript

SESSION_COOKIE_HTTPONLY = False
在您看来,一定要打开
xframe\u options\u emption
,否则django将不允许它从另一个域“iframed”,这里我使用一个模板视图,所以我将decorator放在
url.py

from django.views.decorators.clickjacking import xframe_options_exempt

urlpatterns = [
    path(
        'other_domain/',
        xframe_options_exempt(TemplateView.as_view(template_name='examplesite/otherdomain.html')),
        name='other_domain',
    )
    # ...
]
domains
是所有其他域(不包括用户当前所在的域)的列表,在模板中,在
标记中公开它们

<head>
    {{ domains|json_script:"domains" }}
    {{ other_domain_path|json_script:"other-domain-path"}}
</head>
在脚本中:

(function() {
  function setCookie(cname, cvalue, exdays) {
    var d = new Date();
    d.setTime(d.getTime() + (exdays * 24 * 60 * 60 * 1000));
    var expires = "expires=" + d.toUTCString();
    document.cookie = cname + "=" + cvalue + ";" + expires + ";path=/";
  }
  var domains = JSON.parse(document.getElementById("domains").textContent);

  var trustedSources = domains.map(function(domain) {
    return "https://" + domain;
  });

  window.addEventListener("message", function(e) {
    if (!e.origin in trustedSources) {
      return; // this prevents setting session id from other source
    }
    var sessionID = e.data;
    // you can probably get your cookie expiry from your django view, so all of your cookie expires at the same time
    setCookie("sessionid", sessionID, 365);
  }, false);
})();
现在,您的用户可以从您的任何域登录和注销,并且他们将在您的所有域中拥有相同的会话

我在我的github中发布了完整的示例:


按照
readme.md
进行设置。

如果使用这两种方法,请务必检查“Referer”标题。否则,任何人都可以使用图像标记设置您的用户会话id。例如,我是您网站的用户,然后我登录,获取会话id,然后在我的网站中包含图像标签,任何访问我网站的人都将以我的身份登录。这是真的。我还将使用JWT,到期时间为5分钟左右,cookie会话也将与此令牌不同。过期时间更长。这将确保即使是图像标签也被重复使用。使用此答案的时间过长是无效的
(function() {
  function getCookie(cname) { //copied from w3schools
    var name = cname + "=";
    var decodedCookie = decodeURIComponent(document.cookie);
    var ca = decodedCookie.split(";");
    for (var i = 0; i < ca.length; i++) {
      var c = ca[i];
      while (c.charAt(0) == " ") {
        c = c.substring(1);
      }
      if (c.indexOf(name) == 0) {
        return c.substring(name.length, c.length);
      }
    }
    return "";
  }

  function postSessionID(id) {
    var domains = JSON.parse(document.getElementById("domains").textContent);
    var path = JSON.parse(document.getElementById("other-domain-path").textContent);
    domains.forEach(function(domain) {
      var src = "https://" + domain + path;
      var iframeEl = document.createElement("iframe");
      iframeEl.setAttribute("class", "invisible");
      iframeEl.setAttribute("src", src);
      (function(id) { // this is an async call in a loop, create a closure here to protect "id"
        iframeEl.addEventListener("load", function() {
          this.contentWindow.postMessage(id, this.getAttribute("src"));
        });
      })(id);
      document.body.appendChild(iframeEl);
    });
  }

  function main() {
    var sessionID = getCookie("sessionid");
    if (!sessionID) {
      return;
    }
    postSessionID(sessionID);
  }

  main();

})();
<head>
    {{ domains|json_script:"domains" }}
    {# we also need to expose all other domains #}
</head>
(function() {
  function setCookie(cname, cvalue, exdays) {
    var d = new Date();
    d.setTime(d.getTime() + (exdays * 24 * 60 * 60 * 1000));
    var expires = "expires=" + d.toUTCString();
    document.cookie = cname + "=" + cvalue + ";" + expires + ";path=/";
  }
  var domains = JSON.parse(document.getElementById("domains").textContent);

  var trustedSources = domains.map(function(domain) {
    return "https://" + domain;
  });

  window.addEventListener("message", function(e) {
    if (!e.origin in trustedSources) {
      return; // this prevents setting session id from other source
    }
    var sessionID = e.data;
    // you can probably get your cookie expiry from your django view, so all of your cookie expires at the same time
    setCookie("sessionid", sessionID, 365);
  }, false);
})();