目录
Django 验证系统在默认配置下的使用方法
概况
User对象
创建用户
验证用户
权限和授权
默认权限
用户组
以编程方式创建权限
权限缓存
Web请求中的身份验证
用户如何登陆
选择验证后端
用户如何登出¶
限制对登录用户的访问
原始方式
使用login_required装饰器
LoginRequired Mixin
限制对通过测试的登录用户的访问
最简单的方法
user_passes_test方法
class UserPassesTestMixin
permission_required 装饰器
Django带有用户身份验证系统。它处理用户帐户,组,权限和基于cookie的用户会话。
Django身份验证同时提供身份验证和授权,通常称为身份验证系统,因为这些功能在某种程度上是耦合的。
Django身份验证系统处理身份验证和授权。简而言之,身份验证验证用户是否是他们声称的用户,并且授权确定允许经过身份验证的用户做什么。
认证系统由以下部分组成:
一些常见问题的解决方案已在第三方软件包中实现:
身份验证支持捆绑为Django contrib模块 django.contrib.auth
。
User
对象User对象是身份验证系统的核心。它们通常代表与站点交互的人员,用于启用限制访问,注册用户配置文件,将内容与创建者关联等内容.
Django 的认证框架中只有一个user类,例如 “superuseradmin”或“staff”只是具有特殊属性集的用户对象,而不是用户对象的不同类。默认用户的主要属性是:
创建用户最直接的方法是使用包含 create_user()
的函数:
>>> from django.contrib.auth.models import User
>>> user = User.objects.create_user('john', '[email protected]', 'johnpassword')
# At this point, user is a User object that has already been saved
# to the database. You can continue to change its attributes
# if you want to change other fields.
>>> user.last_name = 'Lennon'
>>> user.save()
方法:authenticate
(request = None,**credentials)
源码 ——def authenticate (request = None , ** credentials ): “”“ 如果给定的凭证有效,则返回一个User对象。 ”“”
使用authenticate()验证用户。将凭据作为参数——默认情况下为用户名和密码,对每个验证后端进行检查,如果凭据对后端有效,则返回用户对象。如果凭证对任何后端无效,或者后端引发PermissionDenied,则返回none。例如:
from django.contrib.auth import authenticate
user = authenticate(username='john', password='secret')
if user is not None:
# A backend authenticated the credentials
else:
# No backend authenticated the credentials
request
是一个可选的HttpRequest
,它在身份验证后端的方法authenticate()
上传递。
《《这个一个很底层的验证方法。比如,可以通过 RemoteUserMiddleware
来验证。除非你在编写自己的身份验证系统,否则你可能不会用到它。如果你正在寻找用户登录的方法,请参阅 LoginView
。》》
注解
这是一种验证一组凭据的低级方法; 例如,它被使用了 RemoteUserMiddleware
。除非您正在编写自己的身份验证系统,否则您可能不会使用它。相反,如果您正在寻找登录用户的方法,请使用 LoginView
。
Django带有一个简单的权限系统。它提供了一种为特定用户和用户组分配权限的方法。
它在 Django 管理后台界面里使用,但你也可以在自己的代码中使用它。
Django 的 admin 页面使用了如下权限:
不仅可以为每个对象类型设置权限,还可以为每个指定对象实例设置权限。通过使用 ModelAdmin
类提供的 has_view_permission()
, has_add_permission()
, has_change_permission()
和 has_delete_permission()
方法,可以为同一类型的不同实例定制权限。
User
对象有两个多对多字段:groups
和 user_permissions
。 User
对象可以像访问其他 :doc:`Django model `: 一样访问他们的相关对象。
myuser.groups.set([group_list])
myuser.groups.add(group, group, ...)
myuser.groups.remove(group, group, ...)
myuser.groups.clear()
myuser.user_permissions.set([permission_list])
myuser.user_permissions.add(permission, permission, ...)
myuser.user_permissions.remove(permission, permission, ...)
myuser.user_permissions.clear()
当 INSTALLED_APPS
设置了 django.contrib.auth
时,它将确保你的每个 Django 模型被创建时有四个默认权限:添加、修改、删除和查看。
运行 manage.py migrate
时将创建这些权限。当你添加 django.contrib.auth
到 INSTALLED_APPS
后第一次运行 迁移
,将会为所有已经安装过的模型以及现在正在安装的模型创建这些默认的权限。之后,每次你运行 manage.py migrate
都会为新模型创建默认权限 (创建权限的函数连接 post_migrate
信号)。
假设你有一个名为 foo
应用程序和一个名为 Bar
的模型,要测试基础权限,你应该使用:
user.has_perm('foo.add_bar')
user.has_perm('foo.change_bar')
user.has_perm('foo.delete_bar')
user.has_perm('foo.view_bar')
权限模型很少会被直接访问。
django.contrib.auth.models.Group
模型是对用户进行分类的通用方法,因此您可以将权限或其他标签应用于这些用户。用户可以属于任意数量的组。
组里的用户会自动拥有该组的权限。
除权限外,组是一个方便的途径,可以给用户分类,为其提供一些标签或扩展功能。例如,你可以创建一个组 'Special users'
,并在编写的代码里让该组成员访问网站仅限会员部分的内容,或者对该组成员发送仅限会员查看的电子邮件。
虽然可以在模型的Meta类中定义权限,也可以直接创建权限。例如,您可以在myapp中为blogpost模型创建can_publish权限:
from myapp.models import BlogPost
from django.contrib.auth.models import Permission
from django.contrib.contenttypes.models import ContentType
content_type = ContentType.objects.get_for_model(BlogPost)
permission = Permission.objects.create(
codename='can_publish',
name='Can Publish Posts',
content_type=content_type,
)
然后,可以通过 user_permissions
属性将权限分配给 User
,或通过 permissions
属性分配给 Group
在第一次需要获取用户对象的权限检查时, ModelBackend
才会缓存它们的权限。对于请求-响应周期来说,这通常是很好的,因为权限通常不会在添加的时候立刻检查(例如,在 admin 中)。如果你打算在测试或视图中添加权限,并随后检查他们,最简单的解决方案就是从数据库中重新获取用户。
Django 使用 sessions 和中间件将身份验证系统挂接到请求对象中。
通过在每次请求中都提供的 request.user
属性来判断用户身份。如果当前没有用户登录,这个属性将会被设置为 AnonymousUser
,否则将会被设置为 User
实例。
你可以使用 is_authenticated
区分两者,例如:
if request.user.is_authenticated:
# Do something for authenticated users.
...
else:
# Do something for anonymous users.
...
如果有一个已验证的用户想附加到当前会话(session)中,将通过 login()
函数完成。
login
(request, user, backend=None)
"""函数描述——
在请求中保留用户ID和后端。这样用户就不会必须对每个请求重新进行身份验证。匿名会话在用户登录时保留
"""
要在视图中让用户登录,使用 login()
。它需要 HttpRequest
对象和 User
对象。通过 Django 的 session 框架, login()
会在 (用户登录之后)session 中保存用户的ID。
《《
》》
注意,在匿名会话期间设置的任何数据都会在用户登录后保留在会话中。
这个例子展示了如何使用 authenticate()
和 login()
: :
from django.contrib.auth import authenticate, login
def my_view(request):
username = request.POST['username'] #获取登录请求中(一般通过表单输入)的username属性
password = request.POST['password'] #获取登录请求中的password属性
user = authenticate(request, username=username, password=password)#验证用户——通过获取的属性值
if user is not None: #当用户验证通过时,user为authenticate()方法返回的user对象
login(request, user) #使用该返回的对象进行登陆操作——记录对象Id和后端加入会话;
# Redirect to a success page.
...
else:
# Return an 'invalid login' error message.
...
当用户登录时,用户 ID 和用于身份验证的后端会被保存在用户会话中。允许相同的 authentication backend 在未来的请求中获取用户详情。选择要在会话中保存的验证后端如下:
backend
参数值。user.backend
的值。允许配对 authenticate()
和 login()
:当返回用户对象时 authenticate()
设置 user.backend
属性。AUTHENTICATION_BACKENDS
存在的 backend
。在1和2中,backend
参数和 user.backend
属性应该是完整的导入路径(像 AUTHENTICATION_BACKENDS
里的路径一样),而不是真实的后端类。
logout
(request)[源代码]¶
如果已经通过 django.contrib.auth.login()
登录的用户想退出登录,可以在视图中使用 django.contrib.auth.logout()
。需要传入 HttpRequest
对象,并且该函数不会返回值。例如:
from django.contrib.auth import logout
def logout_view(request):
logout(request)
# Redirect to a success page.
注意,如果用户未登录,logout()
不会报错。
调用 logout()
后,当前请求的会话数据会被全部清除。这是为了防止其他使用同一个浏览器的用户访问前一名用户的会话数据。如果想在登出后立即向用户提供的会话中放入任何内容,请在调用 django.contrib.auth.logout()
之后执行此操作。
限制访问页面最简单的办法就是检查 request.user.is_authenticated
并重定向到登录页面。
from django.conf import settings
from django.shortcuts import redirect
def my_view(request):
if not request.user.is_authenticated:
return redirect('%s?next=%s' % (settings.LOGIN_URL, request.path))
# ...
或者显示一个错误信息:
from django.shortcuts import render
def my_view(request):
if not request.user.is_authenticated:
return render(request, 'myapp/login_error.html')
# ...
login_required
(redirect_field_name='next', login_url=None)
作为快捷方式,你可以使用 login_required()
装饰器:
from django.contrib.auth.decorators import login_required
@login_required
def my_view(request):
...
login_required()
会执行以下操作:
settings.LOGIN_URL
,并传递绝对路径到查询字符串中。例如: /accounts/login/?next=/polls/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
,你需要确认 settings.LOGIN_URL
和登录视图是正确关联的。例如,使用默认方式,在 URL 配置文件里添加下面这行:
from django.contrib.auth import views as auth_views
path('accounts/login/', auth_views.LoginView.as_view()),
settings.LOGIN_URL
也接受视图方法名和 named URL patterns 。这样你可以在 URLconf 里自由地重新映射你的登录视图,而不需更新配置文件。
注解
login_required
装饰器不会检查用户的 is_active
标识状态,但默认的 AUTHENTICATION_BACKENDS
会拒绝非正常用户。
参见
如果你打算编写自定义的 Django 管理模块视图(或需要与内置视图使用同样的权限检查),你将会发现 django.contrib.admin.views.decorators.staff_member_required()
装饰器是 login_required()
的一个有用的替代方法。
使用基于类的视图时,可以使用 LoginRequiredMixin
实现和 login_required
相同的行为。这个 Mixin 应该在继承列表中最左侧的位置。
如果一个视图使用 Mixin ,那么未经验证用户的所有请求都会被重定向到登录页面或者显示 HTTP 403 Forbidden 错误,这取决于 raise_exception
参数。
你可以设置 AccessMixin
的任何参数来自定义未验证用户的处理:
from django.contrib.auth.mixins import LoginRequiredMixin
class MyView(LoginRequiredMixin, View):
login_url = '/login/'
redirect_field_name = 'redirect_to'
注解
同 login_required
装饰器一样,Mixin 不会检查用户的 is_active
标识状态,但默认的 AUTHENTICATION_BACKENDS
会拒绝非正常用户。
根据某些权限或者其他测试来限制访问,你基本上可以执行和上一节所述同样的操作。
在视图里直接对 request.user
进行测试。举例,这个视图检查用户是否拥有特定域名的邮箱,否则会重定向到登录页:
from django.shortcuts import redirect
def my_view(request):
if not request.user.email.endswith('@example.com'):
return redirect('/login/?next=%s' % request.path)
# ...
作为快捷方式,你可以方便的调用 user_passes_test
装饰器,当调用返回 False
时会执行重定向。
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()
接受一个必要的参数:一个带有:class:~django.contrib.auth.models.User 对象的调用,如果允许用户访问这个页面,则返回 True
。注意,user_passes_test()
不会自动检查用户是否匿名。
user_passes_test()
可以传递两个可选参数:
login_url
允许你指定用户没有通过测试时跳转的地址。它可能是一个登录页面,如果你没指定,默认是 settings.LOGIN_URL
。
redirect_field_name
与 login_required()
相同。如果你想把没通过检查的用户重定向到没有 "next page" 的非登录页面时,把它设置为 None
,这样它会在 URL 中移除。
例如:
@user_passes_test(email_check, login_url='/login/')
def my_view(request):
...
使用基于类的视图时,可以使用 UserPassesTestMixin
执行此操作。
test_func
()¶
你必须覆盖类方法 test_func()
以提供执行的测试。此外,还可以设置 AccessMixin
的任何参数来自定义处理未授权用户:
from django.contrib.auth.mixins import UserPassesTestMixin
class MyView(UserPassesTestMixin, View):
def test_func(self):
return self.request.user.email.endswith('@example.com')
get_test_func
()¶
你也可以覆盖 get_test_func()
方法,以使 mixin 对其检查使用不同名称的函数(而不是 test_func()
)。
Stacking UserPassesTestMixin
由于实现了 UserPassesTestMixin
方式,不能在继承列表中堆砌它们。下述方式将不能工作:
class TestMixin1(UserPassesTestMixin):
def test_func(self):
return self.request.user.email.endswith('@example.com')
class TestMixin2(UserPassesTestMixin):
def test_func(self):
return self.request.user.username.startswith('django')
class MyView(TestMixin1, TestMixin2, View):
...
如果 TestMixin1
调用 super()
并把结果考虑在内,TestMixin1
将不能独立运行。
permission_required
(perm, login_url=None, raise_exception=False)
It's a relatively common task to check whether a user has a particular permission. For that reason, Django provides a shortcut for that case: the permission_required()
decorator.:
from django.contrib.auth.decorators import permission_required
@permission_required('polls.can_vote')
def my_view(request):
...
Just like the has_perm()
method, permission names take the form "
(i.e. polls.can_vote
for a permission on a model in the polls
application).
The decorator may also take an iterable of permissions, in which case the user must have all of the permissions in order to access the view.
Note that permission_required()
also takes an optional login_url
parameter:
from django.contrib.auth.decorators import permission_required
@permission_required('polls.can_vote', login_url='/loginpage/')
def my_view(request):
...
As in the login_required()
decorator, login_url
defaults to settings.LOGIN_URL
.
If the raise_exception
parameter is given, the decorator will raise PermissionDenied
, prompting the 403 (HTTP Forbidden) viewinstead of redirecting to the login page.
If you want to use raise_exception
but also give your users a chance to login first, you can add the login_required()
decorator:
from django.contrib.auth.decorators import login_required, permission_required
@login_required
@permission_required('polls.can_vote', raise_exception=True)
def my_view(request):
...
This also avoids a redirect loop when LoginView
's redirect_authenticated_user=True
and the logged-in user doesn't have all of the required permissions.
The PermissionRequiredMixin
mixin¶
To apply permission checks to class-based views, you can use the PermissionRequiredMixin
:
class PermissionRequiredMixin
¶
This mixin, just like the permission_required
decorator, checks whether the user accessing a view has all given permissions. You should specify the permission (or an iterable of permissions) using the permission_required
parameter:
from django.contrib.auth.mixins import PermissionRequiredMixin
class MyView(PermissionRequiredMixin, View):
permission_required = 'polls.can_vote'
# Or multiple of permissions:
permission_required = ('polls.can_open', 'polls.can_edit')
You can set any of the parameters of AccessMixin
to customize the handling of unauthorized users.
你可能同样需要重写这些方法:
get_permission_required
()¶
Returns an iterable of permission names used by the mixin. Defaults to the permission_required
attribute, converted to a tuple if necessary.
has_permission
()¶
Returns a boolean denoting whether the current user has permission to execute the decorated view. By default, this returns the result of calling has_perms()
with the list of permissions returned by get_permission_required()
.