django自带的用户身份验证启动(官方文档)
django本身自带有一套权限管理系统,通过该权限管理系统,我们可以方便快捷的控制用户的登录权限,访问权限。通过简单的配置,我们就可以使用django的权限管理。
- 编辑setting.py文件的 INSTALLED_APPS,添加 django.contrib.auth 和 django.contrib.contenttypes
- 编辑setting.py文件的 MIDDLEWARE, 添加 SessionMiddleware 和 AuthenticationMiddleware
- 如果是通过django 的 startproject 命令创建的项目的话,上述配置会自动在setting.py中设置
- 执行 python manage.py migrate,django会自动创建相关的用户表以及权限表。
django用户的操作
权限管理的核心,就是用户。django通过各种对用户的操作,来实现对用户权限的控制。
用户基础信息管理
- 你可以通过操作model的方式,来创建用户。操作的model为
from django.contrib.auth.models import User
- 通过操作model来修改密码, 同样的model,要操作的方法为
user = User.objects.get(username='xxx') user .set_password('new password') user.save()
- 通过命令行的方式,创建管理员用户
python manage.py createsuperuser --username=xxx --email=xxx@example.com # --username: 用户名称,必须的字段 # --email: 用户邮箱,必须字段 # 执行完该命令后,会提示你输入密码,输入密码之后,超级用户创建完成 # 两个必填字段如果没有填写,命令也会提示你输入。
- 用户组
用户组可以方便你用来管理的用户,如组内的用户,拥有这个组的权限。或者你可以针对某一个组,做针对性的编码,如定时向管理员组的组员,发送系统运行情况邮件。
相关的组模型为django.contrib.auth.models.Group
用户权限管理
-
用户验证
authenticate(request=None, **credentials)
该方法是用来验证用户有效性的。比如用户名长度,用户名规则,密码长度等。
该方法会将 setting.py 中 AUTHENTICATION_BACKENDS,注册的用户验证方法,遍历验证。
如果通过验证,则返回一个有效的User实例,否则返回None。
你可以自定义改参数,来实现你的自定义用户验证规则。
该参数隐式默认为['django.contrib.auth.backends.ModelBackend']
该方法中的request是HttpRequest的实例,非必须项。其他验证参数,默认是使用username,password
from django.contrib.auth import authenticate user = authenticate(username='xxx', password='111111') if user is not None: # 用户验证通过 else: # 用户验证不通过
-
权限限制
通过ModelAdmin提供的以下四个方法,来判断用户对数据库模型的增、删、改、查权限has_view_permission() has_add_permission() has_change_permission() has_delete_permission()
-
默认权限
当你的setting.py中已经配置了 django.contrib.auth 的时候, 每当你运行migrate命令。django都会对已经安装的app里的model默认添加,增、删、改、查四项权限。
你可以通过下面四个命令来验证,你的用户是否具有对应Model的增删改查权限# 验证 xxx 用户 对 app_name下面的modelname 的增删改查权限 user = User.objects.get(username='xxx') user.has_perm('app_name.add_modelname') user.has_perm('foo.change_modelname') user.has_perm('foo.delete_modelname') user.has_perm('foo.view_modelname')
-
通过代码创建权限
比如你想给你的房子模型,创建一个能否进入的权限。from myapp.models import MyHouse from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType content_type = ContentType.objects.get_for_model(MyHouse) permission = Permission.objects.create( codename='can_accsess', name='Can Access in my house', content_type=content_type, )
如果你想给代理模型添加权限的话,在获取content_type时,要加上 for_concrete_model=false参数
content_type = ContentType.objects.get_for_model(MyHouseProxy, for_concrete_model=False)
-
权限缓存
权限刚刚被设置的时候,是不会更新到当前的用户实例的。比如: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 MyHouse user = get_object_or_404(User, pk=1) content_type = ContentType.objects.get_for_model(MyHouse) permission = Permission.objects.get( codename='can_accesshouse', content_type=content_type, ) user.user_permissions.add(permission) user.has_perm('myapp.can_accesshouse') # 这个结果就是false # 也就是,你信添加的用户权限,不会立即刷新到你当前的user实例里面 # 如果你想要立即使用你的权限,你需要重新获取你的user实例 user = get_object_or_404(User, pk=1) user.has_perm('myapp.can_accesshouse') # 这时的结果就是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')] content_type = ContentType.objects.get_for_model(Student, for_concrete_model=False) # 获取权限列表 student_permissions = Permission.objects.filter(content_type=content_type) print([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 why??????? user.has_perm('app.can_eat_pizzas') # false why??????? user.has_perms(('app.add_student', 'app.can_deliver_pizzas')) # true why??????
-
登录管理
django的每个request请求中,都包含有一个 request.user 用来记录当前用户的状态。如果用户是登录状态,则request.user是User的一个实例,否则它将是AnonymousUser(匿名用户)的实例。
你可以通过request.user的is_authenticated属性,来判断用户是已登录用户还是匿名用户if request.user.is_authenticated: # 已登录用户 else: # 匿名用户
你可以通过,上面讲到的authenticate来验证用户,并将用户登录
from django.contrib.auth import authenticate, login from django.contrib.auth import logout def my_view(request): username = request.POST['username'] password = request.POST['password'] # 默认使用用户名和密码进行验证 # 你也可以自定义验证规则,自定义方式在上文中有 user = authenticate(request, username=username, password=password) if user is not None: # Django的登录操作,可以阅读源码,了解其运行原理 # login将会把用户信息保存到session中 login(request, user) # 登录成功 else: # 登录失败 def logout_view(request): logout(request) # 嗨,baby你退出了 # 该操作会清空当前用户在session中的信息,如果你的用户并没有登录。 # 该操作也不会报异常
django已经将登录操作封装好了,我们可以在自己的视图(views)的前面,通过装饰器的方式使用。
login_required,是可以佩戴参数的,两个参数都是可选参数。redirect_field_name: 验证失败后,通过GET方式,传递给login_url的参数字段,默认为next
login_url: 验证失败后,跳转的登录地址,默认取setting.py中的LOGIN_URL配置from django.contrib.auth.decorators import login_required # 如下设置,如果登录验证失败,则跳转到 登录URL为: /xxx/login?new_field=url_path @login_required(redirect_field_name='new_field',login_url='/xxx/login/') def my_view(request): # do what you want
你也可以通过另一种方式达到login_require的方式
from django.contrib.auth.mixins import LoginRequiredMixin class MyView(LoginRequiredMixin, View): login_url = '/xxx/login/' redirect_field_name = 'new_field'
这两种方式,都不会限制用户的is_active属性,但是默认的AUTHENTICATION_BACKENDS验证,会将is_active=false的用户过滤掉。
-
用户验证
用户验证,是我们常用的一种用户信息验证方式。在进行密码验证之前,我们首先会对用户的用户名,如长度,规则等进行验证,如果验证失败,则不会进行密码验证。通过user_passes_test我们可以实现用户验证。from django.contrib.auth.decorators import user_passes_test def email_check(user): return user.email.endswith('@xxx.com') # user_passes_test的第一个参数为必要参数 # 该参数为一个返回值为User的函数 # 以及两个可选参数:redirect_field_name和login_url、 # user_passes_test的返回值未true/false。如果验证通过,返回true,否则返回false @user_passes_test(email_check) def my_view(request): # 用户验证通过了哦
同样的,这种操作方式,也是有另一种实现方式的。即使用 UserPassesTestMixin
from django.contrib.auth.mixins import UserPassesTestMixin class MyView(UserPassesTestMixin, View): # test_func 方法,是你必须要重写的方法。 # django会根据test_func的内容,来对用户进行处理 # 该方法的返回结果,必须是User的实例 def test_func(self): return self.request.user.email.endswith('@example.com')
-
访问权限
通过permission_required的装饰器,可以实现通过用户权限来限制用户是否可以访问当前的视图(views)from django.contrib.auth.decorators import permission_required # permission_required三个参数 # 第一个参数是用户权限, 如前文所述的用户权限表达方式。 必选参数 # 第二个参数是验证失败后,跳转的登录链接(可以是其他页面链接,如提示无权限访问)。 可选参数 # 第三个参数是控制验证是否报异常,如果为true,则验证失败后,弹出403异常页面,并不会跳转到login_url。 # 可选参数,默认为false @permission_required('polls.can_vote', login_url='/xxx/login/', raise_exception=true) def my_view(request): # 验证通过了哦
-
自定义权限验证
permission_required,login_required,user_passes_test都是继承了AccessMixin,而实现的验证方式,你也通过继承AccessMixin,实现自定义的权限验证。from django.contrib.auth.mixins import AccessMixin class MyRequiredMixin(AccessMixin): # 属性 login_url = 'xxx' # get_login_url的默认返回值,如果没有,则返回setting.py 的 LOGIN_URL redirect_field_name = 'xxx' # get_redirect_field_name 的默认返回值,默认值为'next' permission_denied_message = ‘xxx’ # get_permission_denied_message的默认返回值,默认该值为空 raise_exception = Ture # 是否抛出异常,默认为False。如果为True,则验证失败时,会抛出异常,不会跳转login_url # 方法 def get_login_url(self): # 该方法控制,验证失败时,返回的URL # 默认返回 login_url,如果login_url不存在,则返回 setting.py中的 LOGIN_URL def get_permission_denied_message(self): # 重写该方法,可以控制,当 raise_exception为True时,抛出的异常信息。默认返回permission_denied_message的异常信息 def get_redirect_field_name(self, request, *args, **kwargs): # 用来指定,存放用户登录成功后跳转的URL的字段。 # 如果返回为None,则不会显示该字段 # 默认返回redirect_field_name def handle_no_permission(self, request, *args, **kwargs): # 该函数的行为,取决于raise_exception ,如果raise_exception 为Ture,则抛出异常 # 否则跳转到login_url,并且如果有redirect_field_name,login_url会带有redirect_field_name对应的参数
-
修改密码
如果你修改了用户密码,那么你的用户session会失效。因为django会在你发送request请求的时候,验证根据你的密码生成的session。因为你的密码修改了,所以你的session就失效了。
django本身的修改密码页面和update_session_auth_hash方法,实现了,更改密码,并且保证session不失效。其实就是在修改密码后,直接修改session对应的密码的HMAC。但是前提,你要保证你的用户模型继承了AbstractBaseUser 或者实现了get_session_auth_hash方法,django会通过该方法,获取用户密码的HMAC值。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: pass
-
django提供了登录,退出,密码管理的页面。你可以用现成的,也可以自己定义。
-
其他的权限管理页面,django提供了视图,但是没有提供模板(templates),需要你自己实现并使用