不使用密码手动登录用户


74

希望您能帮我找到无需使用密码即可实现手动(服务器端启动)登录的最佳方法。让我解释一下工作流程:

  • 用户注册
  • 谢谢!带有激活链接的电子邮件已发送blablabla
  • (帐户现已存在,但被标记为未启用)
  • 用户打开电子邮件,单击链接
  • (已启用帐户)
  • 谢谢!您现在可以使用该网站

我要做的是在用户单击电子邮件链接后登录,以便他可以立即开始使用该网站。

我无法使用他的密码,因为该密码已在数据库中加密,这是编写自定义身份验证后端的唯一选择吗?


Answers:


97

您不需要密码即可登录用户。该auth.login功能仅需要一个User对象,在启用帐户后,您大概已经从数据库中获取了该对象。所以你可以直接传递给login

当然,您需要非常小心,用户无法欺骗到现有已启用帐户的链接,然后该链接将以该用户身份自动登录。

from django.contrib.auth import login

def activate_account(request, hash):
    account = get_account_from_hash(hash)
    if not account.is_active:
        account.activate()
        account.save()
        user = account.user
        login(request, user)

...等

编辑

嗯,authenticate因为它增加了额外的属性,所以没有注意到要使用的要求。查看代码,它所做的只是一个backend等同于身份验证后端模块路径的属性。因此,您可以伪造它-在上面的登录调用之前,执行以下操作:

user.backend = 'django.contrib.auth.backends.ModelBackend'

1
谢谢; 文档同意,但也有此警告:“先调用authenticate()手动登录用户时,必须先调用authenticate(),然后再调用login()。authenticate()在User上设置一个属性,指出哪个身份验证后端已成功验证了该用户的身份(有关详细信息,请参阅后端文档),并且此信息将在以后的登录过程中使用。” 这可能是个问题吗?
阿戈斯

尽管最好从django.contrib.conf导入设置并分配设置。如果使用,则使用AUTHENTICATION_BACKENDS具有自定义后端。
Arsham 2014年

这是行不通的,甚至没有修改;您必须创建一个Auth后端并添加到您的设置中;否则,它不会使用户在页面加载之间保持登录状态。
warath-coder,2015年

@ warath-coder你是对的。我确实像Daniel所说的那样,但是在几次ajax请求之后,django服务器响应将cookie会话重置为null,因此用户必须注销。怎么解决呢?
Jcyrss

1
从Django 1.6开始,似乎要求backend设置实际上在其列表中AUTHENTICATION_BACKENDS才能起作用。
蒂姆·提斯达尔

28

从Django 1.10开始,该过程已简化。

在所有版本的Django中,要使用户登录,必须通过您的应用后端之一(由AUTHENTICATION_BACKENDS设置控制)他们进行身份验证

如果您只想强制登录,则可以声明该列表中的第一个后端对用户进行了身份验证:

from django.conf import settings
from django.contrib.auth import login


# Django 1.10+
login(request, user, backend=settings.AUTHENTICATION_BACKENDS[0])

# Django <1.10 -  fake the authenticate() call
user.backend = settings.AUTHENTICATION_BACKENDS[0]
login(request, user)

1
真好!小错字。您有两次设置。应该是login(request, user, backend=settings.AUTHENTICATION_BACKENDS[0])
eupharis '18

@eupharis derp!非常感谢,固定!
伊恩·克拉克

25

丹尼尔的答案非常好。

另一种方法是在自定义授权后端https://docs.djangoproject.com/en/1.8/topics/auth/customizing/#writing-an-authentication-backend后面创建一个HashModelBackend,如下所示:

class HashModelBackend(object):
    def authenticate(self, hash=None):
        user = get_user_from_hash(hash)
        return user

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

然后在您的设置中安装它:

AUTHENTICATION_BACKENDS = (
    'myproject.backends.HashModelBackend',
    'django.contrib.auth.backends.ModelBackend',
)

然后,您的视图将如下所示:

def activate_account(request, hash):
    user = authenticate(hash=hash)
    if user:
        # check if user is_active, and any other checks
        login(request, user)
    else:
        return user_not_found_bad_hash_message

2

丹的回应的回答。

一种写后端的方法:

from django.contrib.auth import get_user_model
from django.contrib.auth.backends import ModelBackend

class HashModelBackend(ModelBackend):

def authenticate(self, username=None, **kwargs):
    UserModel = get_user_model()
    if username is None:
        username = kwargs.get(UserModel.USERNAME_FIELD)
    try:
        user = UserModel._default_manager.get_by_natural_key(username)
        return user
    except UserModel.DoesNotExist:
        return None

答案基于django.contrib.auth.backends.ModelBackend源代码。django 1.9是实际的

我宁愿将自定义后端放在django的默认值之下:

AUTHENTICATION_BACKENDS = [
    'django.contrib.auth.backends.ModelBackend',
    'yours.HashModelBackend',
]

因为与登录本身相比,帐户激活的可能性较小。根据https://docs.djangoproject.com/zh-CN/1.9/topics/auth/customizing/#specifying-authentication-backends

AUTHENTICATION_BACKENDS的顺序很重要,因此,如果相同的用户名和密码在多个后端中有效,则Django将在第一个正匹配项处停止处理。

请注意, 即使密码不正确,此代码也会对您的用户进行身份验证。


此代码中有语法错误,因此尚不清楚应该发生什么(定义了“哈希”但未使用,使用了“用户名”但未定义)。
Amichai Schreiber,

2

您可以使用ska软件包,该软件包具有实现Django的无密码登录的功能。ska与身份验证令牌一起使用,并且其安全性基于SHARED_KEY,这对于所涉及的所有方(服务器)都应相同。

在客户端(请求无密码登录的一方)上,您可以使用生成一个URL并对其进行签名ska。例:

from ska import sign_url
from ska.contrib.django.ska.settings import SECRET_KEY

server_ska_login_url = 'https://server-url.com/ska/login/'

signed_url = sign_url(
    auth_user='test_ska_user_0',
    secret_key=SECRET_KEY,
    url=server_ska_login_url
    extra={
        'email': 'john.doe@mail.example.com',
        'first_name': 'John',
        'last_name': 'Doe',
    }
)

令牌的默认生存期为600秒。您可以通过证明lifetime参数。

在服务器端(用户登录的站点)上,请记住您已ska正确安装,如果存在(创建用户名)或其他方式,则在访问URL时登录用户。您可以在项目的Django设置中自定义3个回调。

  • USER_GET_CALLBACK (字符串):如果成功从数据库(现有用户)获取用户,则触发该事件。
  • USER_CREATE_CALLBACK (字符串):创建用户后立即触发(该用户不存在)。
  • USER_INFO_CALLBACK (字符串):身份验证成功后触发。

有关更多信息,请参见文档(http://pythonhosted.org/ska/)。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.