Django身份验证无需密码

29

我正在使用django的默认身份验证系统,但我添加了一个OpenID库,可以通过OpenID验证用户。 我想要的是登录他们,但使用默认的django身份验证系统似乎需要他们的密码来验证用户。 有没有方法可以绕过这个要求而不实际使用他们的密码?

我想做这样的事情...

user = ... # queried the user based on the OpenID response
user = authenticate(user) # function actually requires a username and password
login(user)

我更愿意跳过 authenticate 函数,但它会附加一个 backend 字段,而这是登录所必需的。


可能是手动登录用户而无需密码的重复问题。 - easoncxz
4个回答

28

编写自定义身份验证后端很简单。如果您创建了yourapp/auth_backend.py文件并包含以下内容:

from django.contrib.auth.backends import ModelBackend
from django.contrib.auth.models import User


class PasswordlessAuthBackend(ModelBackend):
    """Log in to Django without providing a password.

    """
    def authenticate(self, username=None):
        try:
            return User.objects.get(username=username)
        except User.DoesNotExist:
            return None

    def get_user(self, user_id):
        try:
            return User.objects.get(pk=user_id)
        except User.DoesNotExist:
            return None

然后在您的settings.py中添加:

AUTHENTICATION_BACKENDS = (
    # ... your other backends
    'yourapp.auth_backend.PasswordlessAuthBackend',
)

在您的看法中,您现在可以在不需要密码的情况下调用authenticate:

user = authenticate(username=user.username)
login(request, user)

13
这个方法非常有效,但需注意 - 如果您想在某些情况下使用常规的身份验证,以及在其他情况下使用无密码身份验证,请确保防止您的新后端导致每次使用有效用户名的尝试都成功 - 请记住,authenticate() 调用时会尝试所有后端。在我的方法中,我要求包含一个特殊的令牌参数,以确保 authenticate() 的调用者确实希望无密码身份验证能够工作。 - Richard
@Richard,你是如何实现常规认证和无密码认证的?我正在尝试实现它,但它一直在调用常规认证方法。 - Sanket Patel
上述代码在Django 2.1版本中不工作。它没有调用PasswordlessAuthBackend类的authenticate方法。但在Django 2.0中可以工作。 - Sanket Patel
@SanketPatel 我怀疑在较新的Django版本中,def authenticate必须要带有request参数,就像这个问题的另一个回答中所示。 - nh2
@SanketPatel 更多信息请参阅 https://dev59.com/z3A75IYBdhLWcg3wFEsI#54370236。 - nh2

8
这是一种比较巧妙的方法,如果你不想重写大量的内容,可以删除掉认证部分。
user.backend = 'django.contrib.auth.backends.ModelBackend'
login(request, user)

“user”将是您的用户对象。


4
这是一个可能的解决方案,但它不会被保存在会话中。因此,如果你打开新标签并访问该网站,你必须再次登录。 - voodoogiant
1
这将在会话中保存,并在后续视图中起作用。唯一需要注意的是,您设置的后端必须包含在AUTHENTICATION_BACKENDS设置中。我希望我能取消我的反对票,但我不能。 - Emil Stenström
@voodoogiant,我遇到了完全相同的问题,即会话未被存储。但是很奇怪,前两个XHR请求可以正确地从session-id获取会话数据。但是后来的XHR请求却不能,并且Django服务器在响应中将session-id设置为空字符串。你知道为什么吗?如何解决这个问题? - Jcyrss

7

为了实现无需密码验证,在你的settings.py文件中:

AUTHENTICATION_BACKENDS = [
# auth_backend.py implementing Class YourAuth inside yourapp folder
    'yourapp.auth_backend.YourAuth', 
# Default authentication of Django
    'django.contrib.auth.backends.ModelBackend',
]

在你的auth_backend.py文件中:

注意:如果你有自定义的应用程序模型,那么从.models导入CustomUser

from .models import User 
from django.conf import settings

# requires to define two functions authenticate and get_user

class YourAuth:  

    def authenticate(self, request, username=None):
        try:
            user = User.objects.get(username=username)
            return user
        except User.DoesNotExist:
            return None
        
    def get_user(self, user_id):
        try:
            return User.objects.get(pk=user_id)
        except User.DoesNotExist:
            return None

在您的自定义登录请求视图中:
# Your Logic to login user
userName = authenticate(request, username=uid)
login(request, userName)

如需进一步参考,请使用此处提供的Django文档


1
感谢 @OliviaStork 格式化以提高可读性。 - Sameer Kumar Choudhary
我可以只在项目的一个应用程序中使用它,而不将其应用于项目的其他部分吗? - Timothyjames67
'uid'未定义。你指的是什么 'uid'? - Antonio Sanchez
'uid'是用于在逻辑中存储用户ID的自定义参数。 - Sameer Kumar Choudhary
@Timothyjames67 是的,你可以创建自定义用户模型。 - Sameer Kumar Choudhary

2

您可以通过创建自己的认证后端并将其添加到AUTHENTICATION_BACKENDS设置中,轻松解决此问题。

已经提供了一些OpenID后端,因此进行一些搜索后,您可以节省编写一个后端的麻烦。


我在使用Django OpenID后端时遇到了问题,因为它们与Google的OpenID独有方式不兼容。但无论如何,就像我所说,我已经拥有了基于密码的后端,并且我只想在某些情况下使用OpenID--而不是切换到严格的OpenID后端。 - voodoogiant
根据关键字参数选择身份验证后端。因此,如果您在OpenID后端中使用openid_token,并在普通身份验证系统中使用usernamepassword,则两者都可以正常工作。 - Wolph

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