Django丨Authentication 用户认证系统
Django丨Authentication 用户认证系统
1. 介绍🍀
Django自带一个用户认证系统,用于处理用户账户、群组、许可和基于cookie的用户会话。
Django这个认证系统 , 它提供了认证 (authentiaction) 和 授权功能 (authorization) , 这两种功能在某些地方时耦合的。
默认情况下,当项目创建了后,认证的相关模块已经自动添加到settings文件内了:
在INSTALLED_APPS配置中:
django.contrib.auth:包含认证框架的核心以及默认模型;django.contrib.contenttypes:内容类型系统,用于给模型关联许可;
在MIDDLEWARE配置中:
django.middleware.security.SecurityMiddleware:通过请求管理会话;django.contrib.auth.middleware.AuthenticationMiddleware:将会话和用户关联;
2. User对象 🍀
User对象是认证系统的核心,它们通常表示与你的站点进行交互的用户 , 并用于启用限制访问 , 注册用户信息和给创建者关联内容等
用户模型主要有下面几个字段:username、password、email、first_name、last_name
2.1 创建用户🍀
创建用户的几个方法:
1、create:
创建一个普通用户,密码是明文的。
2、create_user:
创建一个普通用户,密码是密文的。(可以不指定email)
1
2
3
4
5
6
7from django.contrib.auth.models import User
user = User.objects.create_user(username='',password='',email='')
user.save()
# 这时候,user是一个User类的实例,已经保存在数据内,你可以随时修改它的属性,例:
user.last_name = 'jeremy'
user.save() # 当修改属性的时候,需要 save()3、create_superuser:
创建一个超级用户,密码是密文的,必须要指定
email参数
(跟上面的方法一样)
2.2 修改密码🍀
django默认会对密码进行加密,因此不能直接对密码字段进行直接操作。
修改密码有两个方法:
使用命令行:
python manage.py changepassword username- 提供了一种从命令行更改用户密码的方法 , 它提示你修改一个给定的user密码 , 你必须输入两次 , 如果两次输入匹配 , 密码就会立即被修改 , (如果你没有提供user , 命令行将尝试修改与当前系统用户匹配的用户名的密码)
使用
set_password()方法;1
2
3
4from django.contrib.auth.models import User
u = User.objects.get(username='john')
u.set_password('new password')
u.save()如果你安装了Django admin , 可以在身份验证系统的管理页面上更改用户的密码
Django还提供视图和表单允许用户修改他们自己的密码
注意 : 更改用户密码将会注销所有会话 , 详见 : Session invalidation on password change
2.3 check_password(passwd)🍀
用户需要修改密码的时候 首先要让他输入原来的密码 ,如果给定的字符串通过了密码检查,返回 True
例如:
1 |
|
2.4 用户认证🍀
使用authenticate()来验证一组凭证,它接收关键字参数credentials,默认为username,password
1 | authenticate(request=None, **credentials): |
根据每一个认证的后端去验证,
如果某个后端凭证通过则返回一个User对象,如果凭证对后端都无效,则主动触发PermissionDenied,并返回None;
1 | from django.contrib.auth import authenticate |
3. 权限和授权🍀
django本身提供一个简单的权限系统,它提供了一种为特定用户和用户组分配权限的方法。
Django中的admin站点也使用了该权限系统 , 使用的权限如下 :
- 查看
add表单和添加对象仅限于具有add权限的用户类型对象 - 查看
change表单和更改对象仅限于具有change权限的用户类型对象 - 删除一个对象仅限于具有
delete权限的用户类型对象
权限不但可以根据每个对象的类型 , 而且可以根据特定的对象实例设置 , 通过ModelAdmin提供的has_add_permission(), has_change_permission()和 has_delete_permission()方法 , 可以针对相同类型的不同对象实例自定义权限
User对象具有两个多对多字段 : groups和user_permissions
User对象可以使用和其他Django模型一样的方式访问他们相关联的对象 , 如下:
1 | myuser.groups.set([group_list]) |
3.1 默认权限🍀
当django.contrib.auth在你的INSTALLED_APPS配置中列出时 , 它将确保为你安装的应用中的每个Django模型创建3个默认的权限 , 即add,change和delete
假设你现在有个app叫做foo,有个model叫做bar,使用下面的方式可以测试默认权限:
1 | add: user.has_perm('foo.add_bar') |
当你运行manage.py migrate时, 将创建这些权限 ; 在django.contrib.auth添加INSTALLED_APPS之后 , 首次运行migrate时 , 将为所有先前安装的模型创建默认权限 , 以及当时安装的任何新模型之后 , 每次运行manage.py migrate, 它将为新的模型创建默认权限(创建权限的函数与post_migrate信号连接)
3.2 用户组🍀
django.contrib.auth.models.Group模型是一种对用户进行分类的通用方式 , 通过这种方式你可以引用权限或其他标签都这些用户 ;
一个用户可以属于任意多个组, 组中每个用户自动具有该组的权限, 例如, 如果Site editors组具有can_edit_home_page权限 , 那么该组中的任何用户都具有该权限
除了权限之外, 组还是给分类用户分配标签,添加功能的便捷方法; 例如, 你可以创建一个组Special users, 只有在该组中的用户才能够访问会员的页面
3.3 编程方式创建权限🍀
虽然我们可以在模型的Meta类中自定义权限, 但是你也可以直接创建权限, 例如, 你可以在myapp中为BlogPost模型创建can_publish权限:
1 | from myapp.models import BlogPost |
然后该权限可以通过user_permissions属性或者通过Group的permissions属性分配给用户
3.4 权限缓存🍀
ModelBackend在第一次需要访问User对象的权限时会对权限进行缓存, 由于对新添加的权限并不会立即检查, 所以这种做法对request-response循环是非常有利的 (例如在admin中), 如果你想要在添加新的权限后马上在测试或视图检查他们, 最简单的解决办法是从数据库中重新获取User, 如下 :
1 | from django.contrib.auth.models import Permission, User |
4. Web请求中的认证 🍀
Django使用Sessions和Middleware来拦截request objects到认证系统中
认证系统为每个请求提供一个request.user属性来代表当前的用户,
如果当前的 用户未登录 或者 用户注销 后 , 该属性将会被赋值给AonnymousUser(匿名用户) 实例 , 否则该属性将会是一个User实例
is_authenticated属性可以判断用户是否登录,这样可以做到对未登录的用户进行限制访问的页面。
我们可以使用is_authenticated属性来进行区分:
1 | if request.user.is_authenticated: |
4.1 登录用户 🍀
如果你有一个经过身份验证的用户 , 你想把它附带到当前的会话中 , 可以通过login()函数完成
1 | login(request, user, backend=None): |
login()使用Django的Session框架来将用户的ID保存在session中
注意 , 匿名会话期间的任何数据集在用户登录后都会保留在会话中
1 | from django.contrib.auth import authenticate, login |
4.2 选择验证后端 🍀
用户登录时, 用户的ID和用于身份验证的后端保存在用户的会话中, 这允许相同的身份验证后端在将来的请求中获取用户的详细信息. 保存会话中的认证后端选择如下:
- 使用可选的
backend参数的值 (如果提供) - 使用
user.backend属性的值 (如果存在) - 如果只有一个, 则使用
AUTHENTICATION_BACKENDS中的后端 - 否则, 触发异常
4.3 登出用户-logout 🍀
要登出一个已经通过django.contrib.auth.login()登入的用户 , 可以在视图中使用django.contrib.auth.logout()
1 | logout(request): |
实例:
1 | from django.contrib.auth import logout |
调用logout()时, 当前请求的会话数据将被彻底清理, 这是为了防止另外一个人使用相同的Web浏览器登入并访问前一个用户的会话数据, 如果你想在用户登出之后可以立即访问放入会话中的数据, 则需要在调用django.conruib.auth.logout()之后放入
4.4 限制访问页面 🍀
很多时候,我们要区分 已登录用户 和 未登录用户,只对登录的用户开放一些页面,限制未登录用户的行为。办法有很多,下面是主要的几种:
- 原始方式 🍀
限制访问页面的简单原始方法时检查request.user.is_authenticated, 并重定向到登录页面:
1 | from django.conf import settings |
或者显示一个错误信息:
1 | from django.shortcuts import render |
- 使用装饰器 🍀
原型:login_required(redirect_field_name='next', login_url=None)[source]
被该装饰器装饰的视图,强制要求用户必须登录后才可以访问。
1 | from django.contrib.auth.decorators import login_required |
出错请注意:
下面。
该装饰器工作机制:
- 如果用户未登陆,重定向到
settings.LOGIN_URL,传递当前绝对路径作为url字符串的参数,例如:/accounts/login/?next=/polls/3/ - 如果用户已经登录,执行正常的视图
此时,默认的url中使用的参数是next,如果你想使用自定义的参数,请修改login_required()的redirect_field_name参数,如下所示:
1 | from django.contrib.auth.decorators import login_required |
如果你这么做了,你还需要重新定制登录模板,因为它引用了redirect_field_name变量。
login_required()装饰器还有一个可选的longin_url参数。例如:
1 | from django.contrib.auth.decorators import login_required |
注意:如果不指定login_url参数,请确保你的settings.LOGIN_URL和登陆视图保持正确的关联。例如:
1 | from django.contrib.auth import views as auth_views |