赞
踩
Django 认证系统处理验证和授权:
这里的认证用于指代这两个任务。这两个任务都依赖于 User 模块。
具体来说这个认证系统负责处理用户账号、组、权限和基于 cookie 的用户会话。
实现时由以下部分组成:
User 对象是认证系统的核心,它通常代表了能与网站进行交互的人员,并用于允许诸如限制访问、注册用户、将内容与创建者关联等功能。
实现时 Django 的认证框架中只使用内置的 User 类,因此,无论是超级管理员还是普通用户,都只是被设置了特殊属性集的 User 对象。
User 对象模型的源码位于django\contrib\auth\models.py
,具体继承关系如下:
class AbstractBaseUser(models.Model):
class PermissionsMixin(models.Model):
class AbstractUser(AbstractBaseUser, PermissionsMixin):
class User(AbstractUser):
User 对象的字段、属性与方法请参考官方文档:User model
创建普通用户最直接的方法是使用 create_user()
函数:
>>> from django.contrib.auth.models import User
>>> user = User.objects.create_user('john', 'lennon@thebeatles.com', 'johnpassword')
# 此时,user是一个已经保存到数据库中的User对象,但可以继续更改其属性
>>> user.last_name = 'Lennon'
>>> user.save()
多数情况下,会先自定义用户模型,然后配合 ModelForm
从页面收集经过验证的用户数据,最后调用表单实例的方法来创建用户。
model.py: class MyUser(AbstractUser): qq = models.CharField('QQ号码', max_length=16) weChat = models.CharField('微信账号', max_length=100) mobile = models.CharField('手机号码', max_length=11) def __str__(self): return self.username forms.py: class MyUserCreationForm(UserCreationForm): class Meta(UserCreationForm.Meta): model = MyUser fields = UserCreationForm.Meta.fields fields += ('email', 'mobile', 'weChat', 'qq') views.py: # 使用表单实现用户注册 def registerView(request): if request.method == 'POST': user = MyUserCreationForm(request.POST) if user.is_valid(): user.save() tips = '注册成功' else: tips = '注册失败' user = MyUserCreationForm() return render(request, 'user.html', locals()) user.html: <!DOCTYPE html> <html lang="zh-cn"> <head> <meta charset="utf-8"> <title>用户注册</title> <link rel="stylesheet" href="https://unpkg.com/mobi.css/dist/mobi.min.css"> </head> <body> <div class="flex-center"> <div class="container"> <div class="flex-center"> <div class="unit-1-2 unit-1-on-mobile"> <h1>用户注册</h1> {% if tips %} <div>{{ tips }}</div> {% endif %} <form class="form" action="" method="post"> {% csrf_token %} <div>用户名:{{ user.username }}</div> <div>邮 箱:{{ user.email }}</div> <div>手机号:{{ user.mobile }}</div> <div>Q Q 号:{{ user.qq }}</div> <div>微信号:{{ user.weChat }}</div> <div>密 码:{{ user.password1 }}</div> <div>密码确认:{{ user.password2 }}</div> <button type="submit" class="btn btn-primary btn-block">注 册</button> </form> </div> </div> </div> </div> </body> </html>
创建超级用户最直接的方法是使用 createsuperuser
命令:
$ python manage.py createsuperuser
Username (leave blank to use 'administrator'): joe
Email address: joe@example.com
Password:
Password (again):
Superuser created successfully.
当然也能使用 create_superuser()
函数来创建超级用户:
create_user()
相同,但将 is_staff
和 is_superuser
设置为 True。>>> from django.contrib.auth.models import User
>>> user = User.objects.create_superuser('root', 'root@root.com', 'root')
>>> user
<User: root>
>>> user.save()
>>> user.is_superuser
True
超级用户可以在管理后台创建用户。
Django 不会在用户模型里保存原始明文密码,而只会存储经过计算的哈希值,因此不要试图直接操作用户的密码,这就是创建用户需要辅助函数的原因。
修改用户密码最直接的方法是使用 changepassword
命令:
$ python manage.py changepassword root
Changing password for user 'root'
Password:
Password (again):
Password changed successfully for user 'root'
也可以使用 set_password()
方法:
>>> from django.contrib.auth.models import User
>>> user = User.objects.get(username='john')
>>> user.set_password('new password')
>>> user.save()
超级用户可以在管理后台修改用户密码。
可以调用 authenticate()
根据 username 和 password 这两个参数来验证用户名参是否一致:
django.contrib.auth.models.User
对象,否则返回 None 。is_authenticated
属性返回 True 。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
删除用户最直接的方法是用超级用户登陆管理后台进行删除。
也能使用用户实例的 delete()
方法删除用户:
>>> from django.contrib.auth.models import User
>>> user = User.objects.get(username='john').delete()
>>> user
(1, {'user.User': 1})
实际上,只要是通过编程的方式创建、修改、删除用户,出于安全考虑,都需要经过用户认证并拥有相关权限才能实现。
django.contrib.auth.models.Group
与 Linux 系统中的用户组的概念相同:
User
通过从 PermissionsMixin
中继承的 groups
字段与 Group
产生多对多关联。它俩本身各自是一张数据表,然后两张表的多对多关系由 auth_user_groups
数据表维护。
Group
对象可以通过 user_set
反向查询用户组中的用户。Group 模型和其他 Django 模型 一样拥有标准的数据访问方法:
# 创建组
group = Group.objects.create(name="test")
group = Group.objects.get(name="test") user = User.objects.get(username="admin") # 添加用户到组 group.user_set.add(user) # user.groups.add(group) # user.groups.set([group_list]) # 批量设置 # 从组中删除用户 group.user_set.remove(user) # user.groups.remove(group) # 用户是否在组中 group.user_set.get(user) # user.groups.get(group) # 获取用户所在的所有组 user.groups.all() # 获取组中所有用户 group.user_set.all() # 用户退出所在的所有组 user.groups.clear() # 组清空所有用户 group.user_set.clear()
实际开发中,多将组用于给一批用户赋予权限,前提是组拥有这些权限。当然也能直接给用户指定权限。
User
通过从 PermissionsMixin
中继承的 user_permissions
字段与 Permission
产生多对多关联。它俩本身各自是一张数据表,然后两张表的多对多关系由 auth_user_user_permissions
数据表维护。
Permission
对象可以通过 user_set
反向查询有该权限的用户。Group
通过自身的的 permissions
字段与 Permission
产生多对多关联。它俩本身各自是一张数据表,然后两张表的多对多关系由 auth_group_permissions
数据表维护。
Permission
对象可以通过 group_set
反向有该权限的组。自此,理清用户、用户组和权限之间的关系:
Permission 模型和其他 Django 模型 一样拥有标准的数据访问方法:
from django.contrib.auth.models import Permission
from django.contrib.contenttypes.models import ContentType
# 创建权限
content_type = ContentType.objects.get_for_model(MyModel)
permission = Permission.objects.create(
codename='test_model',
name='Can Test Model',
content_type=content_type,
)
# 删除权限
permission.delete()
user = User.objects.get(username="user") permission1 = Permission.objects.get(codename="permission1") permission2 = Permission.objects.get(codename="permission2") # 向用户添加权限 user.user_permissions.add(permission1, permission2, ...) # user.user_permissions.set([permission_list]) # 判断用户是否有某项权限 user.has_perm('MyModel.codename') # user.has_perms(permission1, permission2, ...) # 获取用户拥有的一切权限 user.get_all_permissions() # 获取用户直接拥有的用户权限 user.get_user_permissions() # 获取用户间接拥有的组权限 user.get_group_permissions() # 删除用于的权限 user.user_permissions.remove(permission1, permission2, ...) # 清空用户所拥有的一切权限 myuser.user_permissions.clear()
group = Group.objects.get(name="test")
permission1 = Permission.objects.get(codename="permission1")
permission2 = Permission.objects.get(codename="permission2")
# 向组添加权限
group.permissions.add(permission1, permission2, ...)
# 获取组的所有权限
group.permissions.all()
# 删除组的权限
group.permissions.remove(permission1, permission2, ...)
# 清空组所拥有的一切权限
group.permissions.clear()
Django 内置了一个权限系统,它提供了为指定的用户和用户组分配权限的方法。
当 INSTALLED_APPS
安装了 django.contrib.auth
时,权限系统将确保你的每个 Django 模型在被创建后有四个默认权限:添加add、修改change、删除delete和查看view。
post_migrate
信号,信号调用相关的权限创建函数为模型创建这四个默认权限。一般来说,add、change、delete 和 view 是最基础的四种权限。实际上,为了给模型创建其他的操作数据能力的权限时,会直接在模型的 Meta 中进行指定,这个放到后面的自定义权限来说。
但无论如何创建的权限,始终都需要分配到具体的用户或者组上。
Django会缓存每个用户对象,包括其权限user_permissions
。
当你在代码中手动改变一个用户的权限后,你必须重新获取该用户对象才能获取最新的权限。
比如:
from django.contrib.auth.models import Permission, User from django.contrib.contenttypes.models import ContentType from django.shortcuts import get_object_or_404 from myapp.models import BlogPost def user_gains_perms(request, user_id): user = get_object_or_404(User, pk=user_id) # any permission check will cache the current set of permissions user.has_perm('myapp.change_blogpost') content_type = ContentType.objects.get_for_model(BlogPost) permission = Permission.objects.get( codename='change_blogpost', content_type=content_type, ) user.user_permissions.add(permission) # Checking the cached permission set user.has_perm('myapp.change_blogpost') # False # Request new instance of User # Be aware that user.refresh_from_db() won't clear the cache. user = get_object_or_404(User, pk=user_id) # Permission cache is repopulated from the database user.has_perm('myapp.change_blogpost') # True ...
如果说授权系统是对模型对象设置并授予权限,那么验证系统则提供了许多可在视图中通用化使用的验证方式来简化身份验证和权限验证的逻辑。
尽管不提供一些常见的 web 验证系统的特性,但其中一些常见问题的解决方案已在第三方插件中实现,比如:
之所以无法将验证和授权完整分离,是因为 django 在实现这两项机制的时候,共用一套验证后端产生了必要的耦合。
验证后端是所有的验证与授权相关的逻辑的具体实现,但却是可插拔的。
Django 维护了一个验证后端列表来用于检查验证,具体使用哪些验证后端则需要AUTHENTICATION_BACKENDS
配置中指定。
AUTHENTICATION_BACKENDS = ['django.contrib.auth.backends.ModelBackend']
AUTHENTICATION_BACKENDS
的顺序很重要,所以如果同一个用户名和密码在多个后端都有效,Django 会在第一个正向匹配时停止处理。PermissionDenied
异常,则验证流程立马终止,Django 不会继续检查其后的后端。ModelBackend
这是 Django 默认使用的验证后端。
BaseBackend
,并实现了其中的方法:它使用由用户标识符和密码组成的凭证对 AUTH_USER_MODEL
配置指定的用户模型进行验证。
USERNAME_FIELD
指定的字段请参考官方文档:Available authentication backends
Django 使用会话中间件和验证中间件将身份验证系统挂接到请求对象中,具体就是:
SessionMiddleware
通过请求管理 sessions 。AuthenticationMiddleware
使用会话将用户和请求关联。这两个中间件会在每次请求中都会提供 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
对象。这个函数会通过 Django 的 session
框架将用户的 ID 保存在会话中。from django.contrib.auth import authenticate, login
def my_view(request):
username = request.POST['username']
password = request.POST['password']
user = authenticate(request, username=username, password=password)
if user is not None:
login(request, user)
# Redirect to a success page.
...
else:
# Return an 'invalid login' error message.
...
分析 login()
源码:
def login(request, user, backend=None): """ 在请求中保留用户 ID 和后端。这样用户就不必对每个请求重新进行身份验证。请注意,匿名会话期间设置的数据会在用户登录时保留。 """ session_auth_hash = '' if user is None: user = request.user if hasattr(user, 'get_session_auth_hash'): # 获取密码字段的 HMAC。 session_auth_hash = user.get_session_auth_hash() if SESSION_KEY in request.session: if _get_user_session_key(request) != user.pk or ( session_auth_hash and not constant_time_compare(request.session.get(HASH_SESSION_KEY, ''), session_auth_hash)): # To avoid reusing another user's session, create a new, empty # session if the existing session corresponds to a different # authenticated user. request.session.flush() else: request.session.cycle_key() try: backend = backend or user.backend except AttributeError: backends = _get_backends(return_tuples=True) if len(backends) == 1: _, backend = backends[0] else: raise ValueError( 'You have multiple authentication backends configured and ' 'therefore must provide the `backend` argument or set the ' '`backend` attribute on the user.' ) else: if not isinstance(backend, str): raise TypeError('backend must be a dotted import path string (got %r).' % backend) request.session[SESSION_KEY] = user._meta.pk.value_to_string(user) request.session[BACKEND_SESSION_KEY] = backend request.session[HASH_SESSION_KEY] = session_auth_hash if hasattr(request, 'user'): request.user = user rotate_token(request) user_logged_in.send(sender=user.__class__, request=request, user=user)
如果已经通过 django.contrib.auth.login()
登录的用户可以在视图中使用 django.contrib.auth.logout()
函数退出登录:
logout()
时,当前请求的会话数据将被完全清除,这是为了防止另一个人使用同一个 web 浏览器登录并访问前一个用户的会话数据。from django.contrib.auth import logout
def logout_view(request):
logout(request)
# Redirect to a success page.
分析 logput()
源码:
def logout(request):
"""
从请求中删除经过身份验证的用户 ID 并刷新他们的会话数据。
"""
user = getattr(request, 'user', None)
if not getattr(user, 'is_authenticated', True):
user = None
# 在用户登出之前发送信号,这样接收器就有机会找出谁登出了。
user_logged_out.send(sender=user.__class__, request=request, user=user)
# 立即刷新会话数据。
request.session.flush()
if hasattr(request, 'user'):
from django.contrib.auth.models import AnonymousUser
request.user = AnonymousUser()
限制访问页面最原始的办法就是检查 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()
装饰器要求用户必须登录才能执行这个视图。
is_active
标识状态,但默认的 AUTHENTICATION_BACKENDS
会拒绝非正常用户。有两种方法指定登录重定向路径:
settings.LOGIN_URL
配置:绝对路径或 URL 模式 name。viewa: from django.contrib.auth.decorators import login_required @login_required def my_view(request): ... settings: ... LOGIN_URL = "/accounts/login/" urls: from MyApp.views import my_view ... path('accounts/login/', my_view), ...
login_url
参数:绝对路径。from django.contrib.auth.decorators import login_required
@login_required(login_url='/accounts/login/')
def my_view(request):
...
LoginRequiredMixin
实现和 login_required
相同的行为。
is_active
标识状态,但默认的 AUTHENTICATION_BACKENDS
会拒绝非正常用户。用户验证被通过的具体行为由 raise_exception
属性决定:
PermissionDenied
异常。login_url
属性指定的登录页面。from django.contrib.auth.mixins import LoginRequiredMixin
class MyView(LoginRequiredMixin, View):
login_url = '/login/'
为了能根据某些权限或者其他测试来限制一登陆用户的访问能力,可以在视图里使用一些装饰器直接对 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 时执行重定向。
user_passes_test(test_func, login_url=None, redirect_field_name='next')
装饰器参数:
test_func
:必要参数。必须是一个带有django.contrib.auth.models.User
对象的调用。login_url
:指定用户没有通过测试时跳转的绝对地址;如果你没指定,默认是 settings.LOGIN_URL
。from django.contrib.auth.decorators import user_passes_test
def email_check(user):
return user.email.endswith('@example.com')
@user_passes_test(email_check, login_url='/login/')
def my_view(request):
...
UserPassesTestMixin扩展完成与 user_passes_test
相同的效果:
from django.contrib.auth.mixins import UserPassesTestMixin
class MyView(UserPassesTestMixin, View):
login_url = None
def test_func(self):
return self.request.user.email.endswith('@example.com')
检查用户是否拥有特定的权限是一个相对常见的任务。
permission_required(perm, login_url=None, raise_exception=False)
装饰器要求用户必须具有某权限才能执行视图:
perm
:权限名,格式为 app_label .permission_codenamelogin_url
:无权限时所要重定向到的登陆位置。默认是settings.LOGIN_URL
raise_exception
:无权限时是否引发 PermissionDenied
错误,提示 the 403 (HTTP Forbidden) view 而不是跳转到登录页面。from django.contrib.auth.decorators import permission_required
@permission_required('polls.add_choice', login_url='/loginpage/')
def my_view(request):
...
如果既想使用 raise_exception
引发错误,也想给用户重定向登录的机会,那需要合用两个装饰器:
from django.contrib.auth.decorators import login_required, permission_required
@login_required
@permission_required('polls.add_choice', raise_exception=True)
def my_view(request):
...
PermissionRequiredMixin扩展完成与 permission_required
相同的效果:
from django.contrib.auth.mixins import PermissionRequiredMixin
class MyView(PermissionRequiredMixin, View):
permission_required = 'polls.add_choice'
# Or multiple of permissions:
permission_required = ('polls.view_choice', 'polls.change_choice')
所有用于类视图限制访问的扩展都继承AccessMixin
,它被用来配置当访问被拒绝时的视图的基本行为。
1,login_url
属性:默认是 None。get_login_url()
的缺省返回值。
2,permission_denied_message
属性:默认是空字符串。是get_permission_denied_message()
的返回值。
3,redirect_field_name
属性:get_redirect_field_name()
的缺省返回值。默认是 “next” 。
4,raise_exception
属性:如果这个属性被设置为 True ,当条件不被满足的时候会引发 PermissionDenied
异常。如果是 False (默认),匿名用户会被重定向至登录页面。
5,get_login_url()
方法:返回当用户没有通过测试时将被重定向的网址。如果已设置,将返回 login_url
,否则返回 settings.LOGIN_URL
。
6,get_permission_denied_message()
:当 raise_exception
为 True 时,这个方法可以控制传递给错误处理程序的错误信息,以便显示给用户。默认返回 permission_denied_message
属性。
7,get_redirect_field_name()
方法:返回查询参数名,包含用户登录成功后重定向的 URL 。如果这个值设置为 None ,将不会添加查询参数。默认返回 redirect_field_name
属性。
8,handle_no_permission()方法:根据 raise_exception
的值,这个方法将会引发 PermissionDenied
异常或重定向用户至 login_url ,如果已设置,则可选地包含 redirect_field_name
。
尽管我们可以自定义用户登录、注销和密码管理的视图,但这些对于比较通用的验证逻辑,django 都提供了内置的验证类视图供直接使用。
在项目中可以使用不同方法来实现这些视图。最简单的方法就是在 URLconf 中包含 django.contrib.auth.urls
提供的 URL :
urlpatterns = [
path('accounts/', include('django.contrib.auth.urls')),
]
将包含以下 URL 模式:
accounts/login/ [name='login']
accounts/logout/ [name='logout']
accounts/password_change/ [name='password_change']
accounts/password_change/done/ [name='password_change_done']
accounts/password_reset/ [name='password_reset']
accounts/password_reset/done/ [name='password_reset_done']
accounts/reset/<uidb64>/<token>/ [name='password_reset_confirm']
accounts/reset/done/ [name='password_reset_complete']
另一种方法是直接在 URL 中引用特定的验证视图:
from django.contrib.auth import views as auth_views
urlpatterns = [
path('change-password/', auth_views.PasswordChangeView.as_view()),
]
将能直接设置更改视图行为的可选参数:
urlpatterns = [
path(
'change-password/',
auth_views.PasswordChangeView.as_view(template_name='change-password.html'),
),
]
第三种方法,就是通过子类去方便地自定义这些视图。
请参考官方文档:All authentication views
除了能在视图的使用过程中进行身份和权限验证外,在模板的使用过程中也能进行验证:
当模板渲染 RequestContext
对象时,当前登录用户(User
实例或 AnonymousUser
实例)被保存在模板变量 {{ user }}
中:
{% if user.is_authenticated %}
<p>Welcome, {{ user.username }}. Thanks for logging in.</p>
{% else %}
<p>Welcome, new user. Please log in.</p>
{% endif %}
当前登录用户的权限保存在模板变量 {{ perms }}
中:
User.has_module_perms()
的代理.User.has_perm()
的代理.{% if perms.foo %}
<p>You have permission to do something in the foo app.</p>
{% if perms.foo.add_vote %}
<p>You can vote!</p>
{% endif %}
{% if perms.foo.add_driving %}
<p>You can drive!</p>
{% endif %}
{% else %}
<p>You don't have permission to do anything in the foo app.</p>
{% endif %}
{% if in %}
语句来查找权限:{% if 'foo' in perms %}
{% if 'foo.add_vote' in perms %}
<p>In lookup works, too.</p>
{% endif %}
{% endif %}
尽管 django 的认证系统考虑很是周到,但我们还是经常需要自定义认证,包括自定义权限、自定义用户模型、自定义验证方式等等。
这里将简单回顾 django在认证中会做什么,然后开始自定义认证指南。
可按照一定的顺序将可用的验证后端配置为名为 AUTHENTICATION_BACKENDS
的列表值。
当有人调用 django.contrib.auth.authenticate()
时,django 会按顺序尝试对所有的认证后端进行认证。直到所有后端都尝试过,或者当某个后端抛出 PermissionDenied
异常时定制多有验证。
验证后端是一个类,要自定义验证后端,需要像默认的 ModelBackend
那样继承基类 BaseBackend
,并实现了其中的方法:
最重要的就是两个必要的用于身份认证的方法:
get_user(user_id)
:接收一个 user_id ——可以是用户名、数据库 ID 等,总之必须是用户对象的主键——然后返回一个用户对象或 None。authenticate(request, **credentials)
:采用 request
参数和 credentials 作为关键字参数。credentials 可以是相关的验证参数、也可以是一个令牌,总之 authenticate()
应该检查它所得到的凭证,如果凭证有效,则返回一个与这些凭证相匹配的用户对象。如果无效,则应返回 None:from django.contrib.auth.backends import BaseBackend
class MyBackend(BaseBackend):
def authenticate(self, request, username=None, password=None):
# Check the username/password and return a user.
...
class MyBackend(BaseBackend):
def authenticate(self, request, token=None):
# Check the token and return a user.
...
由于 Django admin 与 Django User 对象紧密耦合,为了避免没有提供给 authenticate()
的 reques
可能是 None 的情况,最好方法是为每个存在于后台的用户创建一个 Django User 对象(在你的 LDAP 目录中、外部 SQL 数据库中等等),你可以提前写一个脚本来完成这个任务,或者你的 authenticate
方法可以在用户第一次登录时完成。
下面是一个后端实例,它可以根据 settings.py 文件中定义的用户名和密码变量进行验证,并在用户第一次验证时创建一个 Django User 对象:
from django.conf import settings from django.contrib.auth.backends import BaseBackend from django.contrib.auth.hashers import check_password from django.contrib.auth.models import User class SettingsBackend(BaseBackend): """ 根据 ADMIN_LOGIN 和 ADMIN_PASSWORD 设置进行身份验证。 Use the login name and a hash of the password. For example: ADMIN_LOGIN = 'admin' ADMIN_PASSWORD = 'pbkdf2_sha256$30000$Vo0VlMnkR4Bk$qEvtdyZRWTcOsCnI/oQ7fVOu1XAURIZYoOZ3iq8Dr4M=' """ def authenticate(self, request, username=None, password=None): login_valid = (settings.ADMIN_LOGIN == username) pwd_valid = check_password(password, settings.ADMIN_PASSWORD) if login_valid and pwd_valid: try: user = User.objects.get(username=username) except User.DoesNotExist: # Create a new user. There's no need to set a password # because only the password from settings.py is checked. user = User(username=username) user.is_staff = True user.is_superuser = True user.save() return user return None def get_user(self, user_id): try: return User.objects.get(pk=user_id) except User.DoesNotExist: return None
自定义的验证后端还可以选择性实现一组与权限相关的验证方法。
因为用户模型和它的管理器会把权限查找函数(get_user_permissions()
、get_group_permissions()
、get_all_permissions()
、has_perm()
、has_module_perms()
和 with_perm()
)委托给任何实现了这些函数的认证后端。
所以,如果任何后端之一将一个权限赋予了用户,那么 Django 最终也将该权限赋予这个用户。也就是说,用户最终所拥有的权限将是所有验证后端所赋予用户的权限和。
这个多次赋予权限的流程,会在 has_perm()
或 has_module_perms()
中引发 PermissionDenied
异常时被打断,进而导致验证失败。
后端可以像这样为管理员实现权限:
from django.contrib.auth.backends import BaseBackend
class MagicAdminBackend(BaseBackend):
def has_perm(self, user_obj, perm, obj=None):
return user_obj.username == settings.ADMIN_LOGIN
匿名用户是指那些还没有验证过的用户,也就是说,他们还没有提供任何有效的验证信息。
然而,这并不一定意味着他们就无权做任何事。在最基本的层面上,大多数站点允许匿名用户浏览大部分页面,而且很多站点也允许匿名评论。
尽管 Django 的权限框架没有一个地方可以存储匿名用户的权限,但允许传递给验证后端的用户对象一个 django.contrib.uth.models.AnonymousUser
对象,从而允许后端为匿名用户指定自定义的授权行为。
这对于可重用应用的作者来说特别有用,他们可以将所有的授权问题委托给认证后端,而不是需要配置。
非激活用户是指 is_active
字段设置为 False 的用户。
is_active
字段设置为 False 来注销用户账号而不是直接删除。ModelBackend
和 RemoteUserBackend
验证后端直接禁止这些用户进行验证。如果一个自定义用户模型没有 is_active
字段,那么所有用户都将被允许认证。
如果想让非激活用户能进行身份认证,可以使用 AllowAllUsersModelBackend
或 AllowAllUsersRemoteUserBackend
。
权限系统中对匿名用户的支持,可以实现匿名用户有权限做某事而非激活的认证用户没有权限的场景。
is_active
属性。尽管 Django 的权限框架有一个对象权限的基础,但在核心中没有实现。这意味着对对象权限的检查总是返回 False 或一个空列表(取决于所执行的检查)。
obj
和 user_obj
,并可以适当返回对象级别的权限。Django自带的权限机制是针对模型的,这就意味着一个用户如果对 Article 模型有 change 的权限,那么该用户会获得可对所有文章对象进行修改的权限。如果我们希望实现对单个文章对象的权限管理,我们需要借助于第三方库比如django-guardian。
除了像前面那样单独创建 Permission 模型的数据来创建权限之外,更多的给模型创建权限的方法,就是使用模型 Meta
中的 permissions
属性来创建除默认权限之外的模型权限。举个例子
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。