我应该如何在 Django 应用程序中使用 AAD 实现用户 SSO(使用 Django Microsoft 身份验证后端模块)?

J.B*_*J.B 11 django azure single-sign-on azure-active-directory

我正在开发一个安装了Django Microsoft Auth的 Django (2.2.3) 应用程序,以使用 Azure AD 处理 SSO。我已经能够按照快速入门文档使用我的 Microsoft 身份或我添加到 Django 用户表中的标准用户名和密码登录到 Django 管理面板。这一切都是开箱即用的,很好。

我的问题(真的)只是“我接下来要做什么?”。从用户的角度来看,我希望他们:

  1. 导航到我的应用程序(example.com/ 或 example.com/content) - Django 会意识到它们没有经过身份验证,并且
    • 自动将它们重定向到同一窗口中的 SSO 门户,或
    • 将他们重定向到 example.com/login,这要求他们单击一个按钮,在窗口中打开 SSO 门户(这是默认管理案例中发生的情况)
  2. 允许他们通过 Microsoft 帐户登录和使用 MFA
  3. 一旦成功将它们重定向到我的@login_required页面 (example.com/content)

目前,在我导航的根目录(example.com/),我有这个:

    def index(request):
        if request.user.is_authenticated:
            return redirect("/content")
        else:
            return redirect("/login")
Run Code Online (Sandbox Code Playgroud)

我最初的想法是简单地将其更改redirect("/login")redirect(authorization_url)- 这就是我的问题开始的地方..

据我所知,没有任何方法可以获取上下文处理器的当前实例(?)或microsoft_auth插件的后端来调用authorization_url()函数并将用户从views.py.

好的...然后我想我只是实例化MicrosoftClient生成身份验证 URL的类。这不起作用 - 不是 100% 确定为什么,但它认为这可能与MicrosoftClient后端/上下文处理器上的实际实例使用的某些状态变量与我的实例不一致的事实有关。

最后,我尝试模仿自动/admin页面的作用 - 显示一个 SSO 按钮供用户单击,并在单独的窗口中打开 Azure 门户。经过一番挖掘,我意识到我从根本上遇到了同样的问题 - auth URL 作为内联 JS 传递到管理登录页面模板中,稍后用于在客户端异步创建 Azure 窗口。

作为健全性检查,我尝试手动导航到管理员登录页面中显示的身份验证 URL,并且确实有效(尽管重定向/content没有)。

在这一点上,考虑到我认为自己为自己做的事情有多么困难,我觉得我在以完全错误的方式处理整件事。遗憾的是,我找不到有关如何完成这部分流程的任何文档。

所以,我做错了什么?!

J.B*_*J.B 6

又过了几天,我最终自己解决了这些问题,并对 Django 的工作原理有了更多了解。

我缺少的链接是来自(第三方)Django 模块的上下文处理器如何/在哪里将它们的上下文传递到最终呈现的页面。我没有意识到 microsoft_auth 包中的变量(例如authorisation_url在其模板中使用的)默认情况下也可以在我的任何模板中访问。知道了这一点,我能够实现管理面板使用的相同基于 JS 的登录过程的稍微简单的版本。

假设将来阅读本文的任何人都在经历与我相同的(学习)过程(特别是此软件包),我可能会猜测您接下来会遇到的几个问题......

第一个是“我已经成功登录......我该如何代表用户做任何事情?!”。人们会假设您将获得用户的访问令牌以用于将来的请求,但在编写此包时,默认情况下似乎并没有以任何明显的方式执行此操作。该软件包的文档只能让您登录管理面板。

(在我看来,不是那么明显)答案是您必须设置MICROSOFT_AUTH_AUTHENTICATE_HOOK一个可以在成功身份验证时调用的函数。它将传递登录用户(模型)和他们的令牌 JSON 对象,供您根据需要进行处理。经过一番深思熟虑,我选择使用扩展我的用户模型AbstractUser,并将每个用户的令牌与他们的其他数据一起保留。

模型.py

class User(AbstractUser):
    access_token = models.CharField(max_length=2048, blank=True, null=True)
    id_token = models.CharField(max_length=2048, blank=True, null=True)
    token_expires = models.DateTimeField(blank=True, null=True)
Run Code Online (Sandbox Code Playgroud)

文件

from datetime import datetime
from django.utils.timezone import make_aware

def store_token(user, token):
    user.access_token = token["access_token"]
    user.id_token = token["id_token"]
    user.token_expires = make_aware(datetime.fromtimestamp(token["expires_at"]))
    user.save()
Run Code Online (Sandbox Code Playgroud)

设置.py

MICROSOFT_AUTH_EXTRA_SCOPES = "User.Read"
MICROSOFT_AUTH_AUTHENTICATE_HOOK = "django_app.aad.store_token"
Run Code Online (Sandbox Code Playgroud)

请注意MICROSOFT_AUTH_EXTRA_SCOPES设置,这可能是您的第二个/侧面问题 - 包中设置的默认范围SCOPE_MICROSOFT = ["openid", "email", "profile"]以及如何添加更多内容并不明显。我User.Read至少需要添加。请记住,该设置需要一串空格分隔的范围,而不是列表。

获得访问令牌后,您可以自由地向 Microsoft Graph API 发出请求。他们的Graph Explorer在帮助解决这个问题方面非常有用。