我希望你能帮我找出一种最佳方式来实现手动(由服务器发起)登录,而不使用密码。让我解释一下工作流程:
- 用户注册
- 谢谢!已发送包含激活链接的电子邮件等等
- (账户现在存在但标记为未启用)
- 用户打开电子邮件,点击链接
- (账户已启用)
- 谢谢!您现在可以使用该网站了
我的目标是在用户点击电子邮件链接后立即登录他,以便他可以立即开始使用网站。
由于密码在数据库中被加密,所以我不能使用他的密码,那么唯一的选择是编写自定义身份验证后端吗?
我希望你能帮我找出一种最佳方式来实现手动(由服务器发起)登录,而不使用密码。让我解释一下工作流程:
我的目标是在用户点击电子邮件链接后立即登录他,以便他可以立即开始使用网站。
由于密码在数据库中被加密,所以我不能使用他的密码,那么唯一的选择是编写自定义身份验证后端吗?
登录用户时不需要密码。只需使用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'
backend
设置实际上列在AUTHENTICATION_BACKENDS
列表中才能正常工作。 - Tim Tisdall自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)
login(request, user, backend=settings.AUTHENTICATION_BACKENDS[0])
。 - eupharisDaniel的回答非常好。
另一种方法是创建一个 HashModelBackend,按照自定义授权后端的方式 https://docs.djangoproject.com/en/1.8/topics/auth/customizing/#writing-an-authentication-backend 进行操作,如下所示:
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
activate_account
作为在django的SessionMiddleware
和AuthenticationMiddleware
之后加载的中间件。然后我在django的ModelBackend
之前使用了我的cakend。如果有人需要一个完整的示例,请告诉我,我会创建一个新的答案。 - mrroot5回复丹的答案。
编写后端的一种方法:
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',
]
请注意,即使密码不正确,此代码也会对您的用户进行身份验证。AUTHENTICATION_BACKENDS 的顺序很重要,因此如果相同的用户名和密码在多个后端中有效,则 Django 将停止处理第一个正面匹配。
ska
包,它已经实现了无需密码登录Django。 ska
使用身份验证令牌,并且其安全性基于SHARED_KEY,对于涉及的所有方(服务器)都应该是相等的。ska
生成并签署一个URL。例如: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 设置中,有三个回调函数可供自定义。
USER_GET_CALLBACK
(字符串):如果成功从数据库中获取用户,则触发此函数(现有用户)。USER_CREATE_CALLBACK
(字符串):在用户创建后立即触发此函数(用户不存在)。USER_INFO_CALLBACK
(字符串):在身份验证成功时触发此函数。详见文档 (http://pythonhosted.org/ska/)。
force_authenticate
函数 和APIClient.force_authenticate
方法。 - phoenix