代码之家  ›  专栏  ›  技术社区  ›  cethegeek

在Django中,在登录过程中插入用户实例更改的正确位置是什么?

  •  1
  • cethegeek  · 技术社区  · 16 年前

    背景

    我为引用LDAP服务器的Django应用程序提供了一个自定义身份验证后端。

    一旦我对某人进行身份验证,我们的网络基础设施人员就会在LDAP服务器中提供大量关于该用户的信息-他们的姓氏(例如,如果他们结婚,可以更改姓氏)、他们的电子邮件(也可以更改)以及其他公司特定的信息,这些信息将有助于传输到Django。 auth_user 用于本地引用的表或配置文件表。(*)

    为了利用这些数据,从现在开始,在我们的自定义身份验证方法中,我正在查找(如果它是现有用户登录)或创建新的(如果新用户从未登录到我们的Django应用程序)用户,对其进行任何更改并保存它。

    这对我来说很难闻。 认证应该是在授予访问权限时说“是”或“否”,而不是收集有关要存储的用户的信息。我相信这应该发生在其他地方!

    但我不知道其他地方在哪里…

    我当前的实现还导致用户第一次登录我们的django应用程序时出现问题,因为:

    1. 新用户登录我们的应用程序- request.user 现在有一个用户没有 user.id
    2. 我的自定义身份验证方法保存用户信息。现在用户存在于数据库中
    3. django.contrib.auth.login() 踢进去,然后取回 请求用户 (仍然没有 用户ID 不知道authenticate保存了用户)并尝试将更新保存到上次登录的日期。
    4. 保存失败,因为数据库中已存在该用户名的行(违反唯一约束)

    是的,这只会在用户第一次登录时发生;下次登录时会更新, 请求用户 会有一个 用户ID 一切都很好。

    编辑:我正在调查上面的删除区域。登录代码显然只使用 请求.用户 如果用户为“无”(这是在验证authenticationForm之后得出的),则不应该是。我的代码可能有问题…

    但是让认证不仅仅是,你知道的,认证……

    问题

    在登录过程中,插入用户实例更改的正确位置是什么?

    理想情况下,我可以在自定义的身份验证方法中声明,登录后,从LDAP服务器收集的信息应该写入用户实例,并可能写入用户配置文件实例。

    (*)我对LDAP信息进行本地缓存,因为我不想依赖它的运行来允许用户登录到我的系统;如果LDAP关闭,则最后一个用户名和密码 授权用户 被接受。

    2 回复  |  直到 16 年前
        1
  •  2
  •   thraxil    16 年前

    我通过编写自己的身份验证后端并将其放入authenticate()方法来完成类似的工作。代码是公开的 here . 我还包括一个可插入的“映射器”系统来完成大部分工作,而不仅仅是验证用户身份(例如,从LDAP获取全名,根据我们的认证服务提供的“关联”自动创建组,并将某些用户和关联自动映射到人员/超级用户角色)。

    基本上,身份验证方法如下:

    def authenticate(self, ticket=None):
        if ticket is None:
            return None
        # "wind" is our local auth service
        (response,username,groups) = validate_wind_ticket(ticket)
        if response is True:
            try:
                user = User.objects.get(username=username)
            except User.DoesNotExist:
                user = User(username=username, password='wind user')
                user.set_unusable_password()
                # give plugins a chance to pull up more info on the user
                for handler in self.get_profile_handlers():
                    handler.process(user)
                user.save()
            # give plugins a chance to map affiliations to groups
            for handler in self.get_mappers():
                handler.map(user,groups)
            return user
        else:
            # i don't know how to actually get this error message
            # to bubble back up to the user. must dig into
            # django auth deeper. 
            pass
        return None
    

    所以我非常同意你的观点,认证应该只是一个“是/否”的事情,其他事情也应该发生在其他地方,但是我认为,以Django建立事物的方式,阻力最小的途径就是把它与认证放在一起。我确实建议让您自己的身份验证代码将这些东西委托给插件,尽管这是您可以控制的。

    不过,我只在第一次登录时获取LDAP数据(添加auth_用户行时)。之后,只要他们登录,它就使用本地已有的。这意味着,如果他们的LDAP信息发生变化,它不会自动传播到我的应用程序。为了简单起见,这是我愿意做的一个折衷。

    不过,我不知道为什么您在第一次登录时会遇到问题;我采用了一种非常类似的方法,而且还没有遇到这种问题。可能是因为我的应用程序上的登录过程总是在验证后立即将它们重定向到另一个页面,所以虚拟请求。用户从来没有接触过?

        2
  •  1
  •   Community Mohan Dere    8 年前

    这将是我自己问题的两部分答案。

    1. 在登录过程中,插入用户实例更改的正确位置是什么?

      从Django代码判断,我的当前实现,以及 thraxil 上面的答案,我只能假设 在自定义authenticate()方法中修改用户实例是正常的。

      正如我在问题中所说,我觉得这有点不对劲,但django代码显然假定用户实例可能会被修改,而且在其他地方,在身份验证之后,我找不到其他钩子来应用对用户模型的更改。

      所以,如果你需要一个例子,看看 噻嗪酮 code -在我问题的选定答案中。

    2. 为什么我的实现与 噻嗪酮 并生成唯一约束冲突?

      这个问题很难弄清楚。

      姜戈完全没有问题。 好吧,如果它已经支持多个数据库(它就要出现了,我知道!!!!)我可能不会有问题。

      我有多个数据库,不同的应用程序连接到一个或多个不同的数据库。我正在使用SQL Server 2005(使用Django_-Pydbc)。我想分享 auth_user 所有应用程序之间的表。

      考虑到这一点,我所做的就是 auth 在其中一个数据库中建模,然后为其他数据库中的表创建SQL Server同义词。

      它工作得很好:允许我在使用数据库B时,从中选择/插入/更新/删除 B.dbo.auth_user 好像这是一张真正的桌子;虽然真正发生的是我正在操作 A.dbo.auth_user .

      但它确实在一种情况下崩溃了: 要查找生成的标识,Django_Pyodbc执行以下操作:

      SELECT CAST(IDENT_CURRENT(%s) as bigint) % [table_name]

      这似乎对同义词不起作用。它总是返回空值。所以当在我的 authenticate() 方法我做到了 user.save() ,保存部分工作得很好,但是检索标识列并没有-它将保留ID为“无”的用户实例,这将向django代码指示应该插入该实例,而不是更新该实例。

      解决方法: 我有两个选择:

      a)使用视图而不是同义词(这是我所做的)

      b)在user.save()之后使用 User.objects.get(username=username)

    希望能帮助别人。