Django认证系统 (复制粘贴了许多官方文档,加上自己的理解)
user对象
user对象是django认证系统中的核心,认证系统围绕它展开。Django 使用 sessions 和中间件将身份验证系统挂接到请求对象中。每次请求中,他们都会提供request.user这一属性。
主要属性:
更多属性参见full API documentation
创建用户
最直接的方法:使用create_user函数
>>> from django.contrib.auth.models import User >>> user = User.objects.create_user('john', 'lennon@thebeatles.com', '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()
以及创建超级用户(superuser):在命令行中使用createsuperuser以创建超级用户
$ python manage.py createsuperuser --username=joe --email=joe@example.com
你将会被提示输入密码,完成之后,超级管理员就被创建成功了。如果你没有填写参数 --username <createsuperuser --username> ` or :option:--email <createsuperuser --email>` ,也将会被提示输入这些值。
更改密码¶
Django 不会在用户模型里保存原始(明文)密码,而只会存储哈希值(请参阅文档 如何管理密码 documentation of how passwords are managed ) 。因此,请不要试图直接操作用户的密码,这就是创建用户需要辅助函数的原因。
更改一个用户的密码,你有几个选择:
1.命令行
manage.py changepassword *username*
提供了在命令行修改用户密码的方法。它会提示你输入两次新密码,如果操作成功,新密码就立刻生效。如果你没有提供参数 username ,那么将会尝试修改当前系统用户的密码。
2.在代码中修改:
>>> from django.contrib.auth.models import User >>> u = User.objects.get(username='john') >>> u.set_password('new password') >>> u.save()
如果你已经按照了 Django admin 管理后台,你也可以在管理后台页面修改密码(请参阅 :ref:`authentication system's admin pages ` )。
Django 还提供了允许用户自行修改密码的 :ref:`views ` 和 :ref:`forms ` 。
修改密码将会注销用户的所有会话。查看详情请参阅 密码更改时会话失效 。
验证用户:authenticate(request=None, **credentials)
它需要两个参数:username和password,如果验证有效,即存在该用户,就返回一个user对象,否则返回None
一个简单的验证方法:
from django.contrib.auth import authenticate user = authenticate(username='john', password='secret') if user is not None: # 返回登陆页面 else: # 重定向到登陆后页面
request 是可选的 HttpRequest ,它在身份验证后端上的 authenticate() 方法来传递。
注解(我没看懂)
这个一个很底层的验证方法。比如,可以通过 RemoteUserMiddleware 来验证。除非你在编写自己的身份验证系统,否则你可能不会用到它。如果你正在寻找用户登录的方法,请参阅 LoginView 。
权限和认证
Django 带有一个简单的权限系统。它提供了为指定的用户和用户组分配权限的方法。
它在 Django 管理后台界面里使用,但你也可以在自己的代码中使用它。
Django 的 admin 页面使用了如下权限:查看,修改,删除,添加。
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')
权限模型很少会被直接访问(Q:为什么不?A:面向对象的基本任务之一是封装,接口使得访问者不需要知道模型的细节就可以得到里面的成员)
组¶
django.contrib.auth.models.Group 模型是对用户进行分类的通用方法,因此您可以将权限或其他标签应用于这些用户。用户可以属于任意数量的组。
组里的用户会自动拥有该组的权限。举例,如果 Site editors 组有修改网站首页的权限,那么该组的任何成员都有这个权限。
除权限外,组是一个方便的途径,可以给用户分类,为其提供一些标签或扩展功能。例如,你可以创建一个组 'Special users',并在编写的代码里让该组成员访问网站仅限会员部分的内容,或者对该组成员发送仅限会员查看的电子邮件。
以编程方式创建权限¶
虽然可以在模型的 Meta 类中定义 custom permissions ,你也可以直接创建权限。例如,你可以为 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) #contenttype 是一个高级的类模型,可用于统一调用里面封装的模型。意义在于,模型一多,就不方便一个一个调用 permission = Permission.objects.create( codename='can_publish', name='Can Publish Posts', content_type=content_type, )
权限缓存¶
在第一次需要获取用户对象时, ModelBackend 才会缓存它们的权限。对于请求-响应周期来说,这通常是很好的,因为权限通常不会在添加的时候立刻检查(例如,在 admin 中)。如果你打算在测试或视图中添加权限,并随后检查他们,最简单的解决方案就是从数据库中重新获取用户。例如:
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,*注意这里,刚添加完紧接着检查返回了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 *获取后返回了true*
代理模型¶
代理模型的工作方式和具体模型完全相同。代理模型使用自己的内容类型创建权限。代理模型不会继承其子类的具体模型权限。
class Person(models.Model): class Meta: permissions = [('can_eat_pizzas', 'Can eat pizzas')] class Student(Person): class Meta: proxy = True permissions = [('can_deliver_pizzas', 'Can deliver pizzas')] >>> # Fetch the content type for the proxy model. >>> content_type = ContentType.objects.get_for_model(Student, for_concrete_model=False) >>> student_permissions = Permission.objects.filter(content_type=content_type) >>> [p.codename for p in student_permissions] ['add_student', 'change_student', 'delete_student', 'view_student', 'can_deliver_pizzas'] >>> for permission in student_permissions: ... user.user_permissions.add(permission) >>> user.has_perm('app.add_person') False >>> user.has_perm('app.can_eat_pizzas') False >>> user.has_perms(('app.add_student', 'app.can_deliver_pizzas')) True
Web 请求的认证¶
Django 使用 sessions 和中间件将身份验证系统挂接到请求对象中。
它们在每次请求中都会提供 request.user 属性。如果当前没有用户登录,这个属性将会被设置为 AnonymousUser ,django提供了许多方法来限制AnonymousUser的权限(下文会讲到),如果用户已经登陆,则该属性将会被设置为 User 实例。
你可以使用 is_authenticated 区分两者,例如:
if request.user.is_authenticated: # Do something for authenticated users. ... else: # Do something for anonymous users. ...
用户如何登陆:login(request, user, backend=None)
通过 Django 的 session 框架, login() 会在 session 中保存用户的ID。
注意,在匿名会话期间设置的任何数据都会在用户登录后保留在会话中。
使用authenticate和login以验证用户并登录。
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.
选择验证后端¶
当用户登录时,用户 ID 和用于身份验证的后端会被保存在用户会话(session)中。允许相同的 authentication backend 在未来的请求中获取用户详情。选择要在会话中保存的验证后端如下:
- 使用提供了的可选
backend参数值。 - 使用
user.backend的值。整个方法可以将authenticate()和login()配对:authenticate()设置了它返回的user对象的user.backend属性。 - 使用
AUTHENTICATION_BACKENDS存在的backend。 - 否则,抛出一个异常。
在1和2中,backend 参数和 user.backend 属性应该是完整的导入路径(像 AUTHENTICATION_BACKENDS 里的路径一样),而不是真实的后端类。
用户如何登出:logout(request)
该函数无返回值,并且会删除当前请求的所有会话数据,这是为了防止下一个请求的用户访问到上一个请求的用户的数据
限制访问
限制未登录用户访问
1.使用is_authenticated属性
2.login_required装饰器
from django.contrib.auth.decorators import login_required @login_required def my_view(request):
login_required()装饰器装饰的函数只有已登录的状态下才执行,否则将会重定向到settings.LOGIN_URL,你可以通过login_url参数来修改重定向到的url
如果你使用基于类的views,那么你可以使用method_required(login_required(login_url="xxx"))以实现上述功能,或者使用LoginRequiredMixin
3.LoginRequiredMixin
这个 Mixin 应该在继承列表中最左侧的位置。
- class
LoginRequiredMixin¶ - 如果一个视图使用 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'
限制对通过测试的登录用户的访问
类似于限制未登录用户,你可以根据某些权限和其他测试来进行限制:
1.最简单的方法当然是直接检查user的属性
2.user_passes_test(test_func, login_url=None, redirect_field_name='next')是否执行修饰的函数
test_func参数:传给它一个函数,user_passes_test根据函数返回的true或false来决定是执行下面的函数(true)还是重定向回settings.LOGIN_URL/login_url,如果你想重定向回一个没有nextpage的非登陆页面,那么请把redirect_field_name设置为false
同样,你可以使用UserPassesTestMixin以适应基于类的view
3.permission_required(perm, login_url=None, raise_exception=False)权限检查
perm:要检查的权限形式为<app label>.<permission codename>,即名为<app label>应用下的模型的codename=<permission codename>权限
perm可以是一个可迭代的权限,这将会要求用户拥有迭代的所有权限才会执行函数
raise_exception:设置为true时将会提示403 HTTP Forbidden
一点技巧:
如果你想要使用raise_exception并且给用户登录的机会,那么可以写一个login_required装饰器:
from django.contrib.auth.decorators import login_required, permission_required @login_required @permission_required('polls.can_vote', raise_exception=True) def my_view(request):
如果LoginView的redirect_authenticated_user=True并且用户没有相应的权限时这个方法避免了重定向循环
同样,你可以使用PermissionRequiredMixin来为基于类的view服务。
4.使用AccessMixin以装饰基于类的view:
注意:Mixin系列的类不可以被集成,即,写成
class MyView(TestMixin1,TestMixin2, View): ...
是不会按照预期正常运作的
- class
AccessMixin¶ login_url¶-
get_login_url()的缺省返回值。默认是None,在这种情况下,get_login_url()会回退至settings.LOGIN_URL。
permission_denied_message¶-
get_permission_denied_message()的缺省返回值。默认是空字符串。
redirect_field_name¶-
get_redirect_field_name()的缺省返回值。默认是"next"。
raise_exception¶-
如果这个属性被设置为
True,当条件不被满足的时候会引发PermissionDenied异常。如果是False(默认),匿名用户会被重定向至登录页面。
get_login_url()¶-
返回当用户没有通过测试时将被重定向的网址。如果已设置,将返回
login_url,否则返回settings.LOGIN_URL。
get_permission_denied_message()¶-
当
raise_exception为True时,这个方法可以控制传递给错误处理程序的错误信息,以便显示给用户。默认返回permission_denied_message属性。
get_redirect_field_name()¶-
返回查询参数名,包含用户登录成功后重定向的 URL 。如果这个值设置为
None,将不会添加查询参数。默认返回redirect_field_name属性。
handle_no_permission()¶-
根据
raise_exception的值,这个方法将会引发PermissionDenied异常或重定向用户至login_url,如果已设置,则可选地包含redirect_field_name。
密码更改时会话失效
如果一个用户已经经过验证,那么与用户的会话中将会包含password的HMAC(将密码哈希后形成的验证码)Django在每次请求中都会验证这个哈希值是否与计算出来的哈希值(每次请求时都会计算)相匹配。
Django 包含默认的密码修改视图,PasswordChangeView 和 user_change_password 视图在 django.contrib.auth admin 中,将使用新密码的哈希更新会话,因此用户修改密码后不会被注销。如果你有自定义的密码修改视图,并期望有同样的行为,可以使用 update_session_auth_hash() 函数。
update_session_auth_hash(request, user)
这个函数接受当前请求和从新会话哈希派生时更新的用户对象,并会更新哈希值。它也会替换哈希值因此被盗用的会话cookie会无效。
栗子:
rom 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: ...

浙公网安备 33010602011771号