Django使用会话和中间件将认证系统挂接到请求对象。 这些提供了代表当前用户的每个请求的request.user属性。 如果当前用户尚未登录,则此属性将设置为AnonymousUser的一个实例,否则它将是User的一个实例。 您可以使用is_authenticated()来区分它们,如下所示:
if request.user.is_authenticated():
# Do something for authenticated users.
else:
# Do something for anonymous users.
如何登录用户
要从视图登录用户,请使用login()。 它需要一个HttpRequest对象和一个User对象。 login()使用Django的会话框架将用户的ID保存在会话中。 请注意,匿名会话期间的任何数据集都将在用户登录后保留在会话中。此示例显示如何使用authenticate()和login():
from django.contrib.auth import authenticate, login
def my_view(request):
username = request.POST['username']
password = request.POST['password']
user = authenticate(username=username, password=password)
if user is not None:
if user.is_active:
login(request, user)
# Redirect to a success page.
else:
# Return a 'disabled account' error message
else:
# Return an 'invalid login' error message.
首先调用authenticate()首先,当您手动登录用户时,必须在调用login()之前调用authenticate()。 authenticate()在用户身上设置一个属性,指出哪个认证后端成功认证了该用户,并且此信息在登录过程中稍后需要。 如果您尝试直接登录从数据库检索到的用户对象,则会引发错误。
如何登录用户
要注销通过login()登录的用户,请在您的视图中使用logout()。 它需要一个HttpRequest对象并且没有返回值。 例:
from django.contrib.auth import logout
def logout_view(request):
logout(request)
# Redirect to a success page.
请注意,如果用户未登录,logout()不会引发任何错误。当您调用logout()时,当前请求的会话数据将被彻底清除。 所有现有数据都将被删除。 这是为了防止其他人使用同一个Web浏览器登录并访问先前用户的会话数据。
如果你想把任何东西放到注销后立即可用的会话中,那么在调用logout()之后执行该操作。
限制对登录用户的访问
原始的方式
限制访问页面的简单原始方法是检查request.user.is_authenticated()并重定向到登录页面:
from django.shortcuts import redirect
def my_view(request):
if not request.user.is_authenticated():
return redirect('/login/?next=%s' % request.path)
# ...
...或显示错误消息:
from django.shortcuts import render
def my_view(request):
if not request.user.is_authenticated():
return render(request, 'books/login_error.html')
# ...
login_required装饰器
作为一个捷径,你可以使用方便的login_required()装饰器:
from django.contrib.auth.decorators import login_required
@login_required
def my_view(request):
...
login_required()执行以下操作:
- 如果用户未登录,则重定向到LOGIN_URL,并在查询字符串中传递当前绝对路径。 例如:/ accounts / login /?next = / reviews / 3 /。
- 如果用户登录,则正常执行视图。 视图代码可以假定用户已登录。
默认情况下,用户在成功验证后应该重定向到的路径存储在名为“next”的查询字符串参数中。 如果您希望为此参数使用不同的名称,login_required()将采用可选的redirect_field_name参数:
from django.contrib.auth.decorators import login_required
@login_required(redirect_field_name='my_redirect_field')
def my_view(request):
...
请注意,如果您为redirect_field_name提供值,则很可能还需要自定义您的登录模板,因为存储重定向路径的模板上下文变量将使用redirect_field_name的值作为其键值而不是“next”(缺省值)。 login_required()也采用可选的login_url参数。 例:
from django.contrib.auth.decorators import login_required
@login_required(login_url='/accounts/login/')
def my_view(request):
...
请注意,如果您未指定login_url参数,则需要确保LOGIN_URL和您的登录视图正确关联。 例如,使用默认值,将以下行添加到您的URLconf中:
from django.contrib.auth import views as auth_views
url(r'^accounts/login/$', auth_views.login),
LOGIN_URL还接受视图函数名称和指定的URL模式。 这使您可以在URLconf中自由重新映射登录视图,而无需更新设置。
注意:login_required装饰器不会检查用户的is_active标志。
限制对通过测试的登录用户的访问
要根据某些权限或某些其他测试来限制访问权限,您的操作基本上与上一节中所描述的相同。 简单的方法是直接在视图中的request.user上运行测试。 例如,此视图会检查以确保用户在所需的域中有电子邮件:
def my_view(request):
if not request.user.email.endswith('@example.com'):
return HttpResponse("You can't leave a review for this book.")
# ...
作为一个快捷方式,你可以使用方便的user_passes_test修饰器:
from django.contrib.auth.decorators import user_passes_test
def email_check(user):
return user.email.endswith('@example.com')
@user_passes_test(email_check)
def my_view(request):
...
user_passes_test()需要一个必需的参数:一个可调用的用户对象,如果允许用户查看页面,则返回True。 请注意,user_passes_test()不会自动检查用户是否为匿名用户。 user_passes_test()接受两个可选参数:
-
login_url。 让您指定未通过测试的用户将被重定向到的URL。 它可能是一个登录页面,如果你没有指定登录页面,则默认为LOGIN_URL。
-
redirect_field_name。 与login_required()相同。 将其设置为None会将其从URL中删除,如果您将未通过测试的用户重定向到没有“下一页”的非登录页面,则可能需要执行此操作。
例如:
@user_passes_test(email_check, login_url='/login/')
def my_view(request):
...
permission_required装饰器
检查用户是否具有特定权限是相对常见的任务。 出于这个原因,Django为这种情况提供了一个快捷方式 - permission_required()装饰器:
from django.contrib.auth.decorators import permission_required
@permission_required('reviews.can_vote')
def my_view(request):
...
就像has_perm()方法一样,权限名称的形式为“<app label>.<permission codename>”
(即reviews.can_vote在审查申请中对模型的许可)。 装饰器也可以获取权限列表。 请注意,permission_required()也采用可选的login_url参数。 例:
from django.contrib.auth.decorators import permission_required
@permission_required('reviews.can_vote', login_url='/loginpage/')
def my_view(request):
...
和login_required()装饰器一样,login_url默认为LOGIN_URL。 如果给出raise_exception参数,装饰器将引发PermissionDenied,提示403(HTTP禁止)视图,而不是重定向到登录页面。
会话无效,密码更改
如果您的AUTH_USER_MODEL从AbstractBaseUser继承,或者实现它自己的get_session_auth_hash()
方法,认证会话将包含此函数返回的散列。 在AbstractBaseUser的情况下,这是密码字段的哈希消息验证码(HMAC)。
如果启用了SessionAuthenticationMiddleware,Django会验证与每个请求一起发送的哈希与服务器端计算出的哈希相匹配。 这允许用户通过更改其密码来注销其所有会话。
Django包含的默认密码更改视图,django.contrib.auth.views.password_change()和django.contrib.auth管理中的user_change_password视图,使用新密码哈希更新会话,以便用户更改自己的密码 不会注销。 如果您有自定义密码更改视图并希望有类似的行为,请使用此功能:
django.contrib.auth.decorators.update_session_auth_hash (request, user)
此函数接受当前请求和从中派生新会话哈希的更新用户对象,并适当地更新会话哈希。 用法示例:
from django.contrib.auth import update_session_auth_hash
def password_change(request):
if request.method == 'POST':
form = PasswordChangeForm(user=request.user, data=request.POST)
if form.is_valid():
form.save()
update_session_auth_hash(request, form.user)
else:
...
由于get_session_auth_hash()基于SECRET_KEY,因此更新站点以使用新秘密将使所有现有会话无效。
网友评论