77

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

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

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

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

4

5 回答 5

99

您不需要密码来登录用户。该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)

... ETC。

编辑

authenticate嗯,由于它添加了额外的属性,因此没有注意到使用该要求。查看代码,它所做的只是一个backend与身份验证后端的模块路径等效的属性。所以你可以伪造它——在上面的登录调用之前,这样做:

user.backend = 'django.contrib.auth.backends.ModelBackend'
于 2010-05-07T10:12:31.287 回答
29

从 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)
于 2016-09-07T07:47:39.703 回答
26

丹尼尔的回答很好。

另一种方法是在自定义授权后端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
于 2010-05-07T11:12:32.573 回答
3

回应的回答。

一种编写后端的方法:

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/en/1.9/topics/auth/customizing/#specifying-authentication-backends

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

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

于 2016-05-26T08:13:36.877 回答
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/ )。

于 2013-12-21T03:18:26.957 回答