Django单点登录和Php网站:跨域登录?

3
我正在使用Django构建一个小型应用程序,并且现在是将其集成到一些客户端PHP Web应用程序中的时候了。
我们的客户A来自www.a.com域,处理他自己用户的身份验证,并且可能使用cookie进行会话管理。
如何使来自他域名的已登录用户也可以在我的Django应用程序www.b.com/clientA/上登录?
我知道我可以让他们在我的域名上重新登录,并使用一个authbackend检查A域的凭据,但这意味着用户必须在www.a.com和www.b.com上输入他的登录/密码两次。
由于安全原因,无法从www.a.com域访问cookie。
你会如何处理这个问题?

修改 PHP 应用程序是一个选项吗? - oggy
2
顺便说一句,正确的术语是“Djangonauts”。 - lprsd
4个回答

8
您的想法没错,无法访问来自另一个域的cookie。但如果它在子域中,只要cookie设置正确,就应该能够访问它们。
如果您绝对需要它们在完全不同的域上,这会有点棘手。如果您无法修改现有的PHP代码,您基本上可以忘记它。
一种选择是使用OpenID——这可能是解决此问题最简单的方式,因为PHP和Python都有OpenID库可用。 OpenID将允许您具有类似单一登录的身份验证,并且由于它已经在各种网站上使用,因此已被证明有效。
另一个选项是编写自定义单一登录系统。
基本思路是当用户到达您的站点时,您将其引导到登录站点。这可以是在PHP或Python端,也可以是分开的。在这里,用户将登录,然后登录生成一个秘密密钥-这可以是哈希,随机字符串,任何东西,只要它不可预测-并将用户重定向回主站点。
然后,主站点看到用户有一个密钥,并在幕后向登录站点发送请求以验证用户的密钥。
现在用户已在一个站点上登录。当用户访问第二个站点时,它也会将用户重定向到登录站点。由于用户已经登录,登录站点只需使用新的秘密密钥将用户重定向回来,第二个站点从登录站点验证它,现在用户无需再次输入凭据即可登录。

大多数客户已经拥有他们的身份验证模式,不会使用 OpenID,因为对他们来说太陌生了。我喜欢你的最后一个解决方案,使用第三个中心登录站点,但这可能需要客户做出太多改变,除非我提供 PHP 代码。 - coulix
你可以利用客户现有的PHP身份验证来实现第二个选项。你只需要修改它,使其能够生成密钥并与其他网站进行通信。 - Jani Hartikainen
我有一个小问题不太确定。当登录页面给我一个秘钥时,我将其作为GET变量传递到我的原始页面上。现在我使用Ajax发送请求,可能到登录页面域,询问“您可以检查一下这个秘钥吗?”它会回答“是的,有效”,那么接下来怎么做?我该如何在我的域中记录用户? - coulix
也许可以通过使登录服务器在密钥有效时返回用户名和密钥来实现? - coulix
服务器端使用https?但是我如何确保没有人可以伪造接受发送随机ID的假请求? - coulix
显示剩余2条评论

6

好的,这是如何从PHP验证Django用户或从PHP“读取”Django密码。

我认为OpenID是最好的解决方案,但今天我必须在共享同一数据库的PHP应用程序中验证Django用户,以下是我的解决方法:

<?php

/* Generates crypted hash the same way as Django does */
function get_hexdigest($algorithm, $salt, $raw_password) {
   if (!array_in($algorithm, array('md5', 'sha1'))) {
       return false;
   }
   return $algorithm($salt.$raw_password);
}

/* Checks if password matches the same way Django does */
function check_password($raw_password, $django_password) {
    list($algorithm, $salt, $hsh) = explode('$', $django_password);
    return get_hexdigest($algoritm, $salt, $raw_password) === $hsh;
}

?>

关键是要了解Django保存密码的格式,格式如下:

[算法]$[盐值]$[哈希值]

例如,我有一个用户名为“admin”的用户,密码是“admin”,在auth_user行中的密码字段为:

sha1$63a11$85a93f217a72212b23fb0d5b95f3856db9575c1a

算法是"sha1",随机生成的盐值为"63a11",加密后的哈希值为"85a93f217a72212b23fb0d5b95f3856db9575c1a"。

那么在PHP中如何生成加密后的哈希值呢?只需将盐值和原始密码连接起来,并使用该算法进行哈希运算即可,例如本例中使用的是sha1算法:

<?php 

$salt = '63a11';
$pass = 'admin';

echo sha1($salt.$pass); // prints "85a93f217a72212b23fb0d5b95f3856db9575c1a"

?>

那并不难!我通过阅读Django源代码中相关的代码得到了它。

1

您可以使用 HTTP 重定向来回传递。当用户访问 www.b.com 且没有设置 cookie 时,重定向到 www.a.com/crosslogin?return_to=URL&challenge=stuff。在 a.com 上,检查 cookie 是否已设置,如果已设置,则重定向到 URL?verified=otherstuff

如果您想要防止用户伪造身份验证,则需要挑战-响应加密。a.com 和 b.com 需要设置共享密钥,并使用该密钥加密 stuff。otherstuff 也使用该密钥加密;解密后,它会给出一个元组 (stuff, user)。b.com 可能需要保留重放缓存以确保 otherstuff 只能使用一次。


0

我看到以下选项:

1)像Jani Hartkainen建议的那样使用Open ID。这可能是最好的解决方案。

2)通过http反向代理使用一个域:

使用反向http代理将php应用程序和django应用程序放在同一个域中。这将使您可以访问您的php应用程序的会话cookie。

一旦您在django应用程序中获取了php会话ID,请使用设置为会话cookie的请求运行对PHP应用程序的请求,以检查谁已登录。 不幸的是,这可能需要HTML抓取或在PHP应用程序中实现一个简单的服务,该服务将返回已登录用户的名称。 一旦您获得了已登录的用户,就可以在django应用程序中对其进行授权。

3)通过GET传递PHP会话ID:

修改PHP应用程序,将会话ID作为参数添加到指向您的django应用程序的链接中。 例如,要求客户按以下方式引用您的网站:

<yourwebsite.com>/?client_session_id=<session_id>&client_name=<client_name>

一旦您获取了会话ID,您可以按照第2点所述对用户进行身份验证。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接