在使用django-admin startproject
创建项目后,Django就默认安装了一个采用session实现的认证系统。这是Django相比于其他框架的一大特点:自带认证系统,开箱即用。有人说它方便,有人说它鸡肋,但它作为Django的重要组成部分,学习它有助于我们理解Django框架的核心技术。
Django默认已安装,可以在settings.py
中的INSTALLED_APPS
看到:
contrib翻译为普通发布版。
在MIDDLEWARE
可以看到:
SessionMIDdleware
:session中间件。AuthenticationMIDdleware
:认证中间件。使用python manage.py migrate
后,数据库会新增认证系统的这些表:
认证的英文是authentication,授权的英文是authorization。单词不一样,咋看有点像。认证是指验证用户是谁。授权是指授予已认证用户权限。由于认证授权在某种程序上是耦合的,所以Django把它们统称为“认证”。
认证系统概览认证系统的组成部分如下:
用户权限组密码管理登录相关表单(前后端分离不需要)和视图(接受Web请求并且返回Web响应)可配置的backendDjango框架是MTV模式,类似于MVC模式。Django的VIEw对应MVC的Controller。
以上是Django自带内容,如果需要更多功能,可以安装第三方包:
密码增强校验登录限流OAuth对象级权限(django-guardian)models.User以Article举例,Django是模型级权限,用户只能具有全部文章的权限。django-guardian提供了对象级权限,可以对单篇文章进行授权。
User模型是Django认证系统的核心,它的主要属性包括:
IDusernameemailpasswordis_activeis_superuserlast_logindate_joined创建超级管理员django.contrib.auth.models,在django.db.models之上封装了AbstractBaseUser、AbstractUser、User等模型。
cmd中使用createsuperuser
命令:
$ python manage.py createsuperuser
根据提示输入username、email、password后,就会在数据库中创建1条超管用户。
创建用户方法1 代码创建
在代码中使用create_user()
函数来创建用户:
>>> from django.contrib.auth.models import User# 创建用户并保存到数据库>>> user = User.objects.create_user('john','lennon@thebeatles.com','johnpassword')# 修改其他字段值>>> user.last_name = 'Lennon'>>> user.save()
方法2 管理后台创建
访问http://127.0.0.1:8000/admin/
,用超管登录后,在界面上创建:
方法1 命令行修改
python manage.py changepassword username
根据提示输入旧密码、新密码、确认密码即可。
方法2 代码中修改
>>> from django.contrib.auth.models import User>>> u = User.objects.get(username='john')>>> u.set_password('new password')>>> u.save()
方法3 管理后台修改
框架底层使用authenticate()
函数对用户进行认证:
authenticate(request=None,**credentials)
credentials是用户凭证,如用户名、密码。
示例:
from django.contrib.auth import authenticateuser = authenticate(username='john',password='secret')if user is not None: # A backend authenticated the credentialselse: # No backend authenticated the credentials
如果认证成功,authenticate()
会返回User。如果用户凭证无效或者权限不足,认证后端抛出了PermissionDenIEd,authenticate()
会返回None。
认证后端(authentication backends)是Django做用户验证的后端模块,默认为['django.contrib.auth.backends.ModelBackend']
,只会简单比较请求的用户名密码和数据库中的用户名密码是否匹配。可以切换成其他认证后端,也可以重写authenticate()
进行自定义。我点开了源码,发现除了Django的认证后端,DRF已经封装了Session、Token、JWT的认证:
权限一般分为add、change、delete、vIEw,也就是增删改查。
默认权限Django会在python manage.py migrate
的时候,为每个model创建4种权限:add、change、delete、vIEw。
比如有个app叫做foo,它有个model叫做bar,可以使用has_perm()
函数来检查权限:
除了增删改查权限,有时我们需要更多的权限,例如,为myapp中的BlogPost创建一个can_publish权限:
方法1 Meta中配置
class BlogPost(models.Model): ... class Meta: permissions = ( ("can_publish","Can Publish posts"), )
方法2 使用create()
函数
from myapp.models import BlogPostfrom django.contrib.auth.models import Permissionfrom django.contrib.ContentTypes.models import ContentTypecontent_type = ContentType.objects.get_for_model(BlogPost)permission = Permission.objects.create( codename='can_publish',name='Can Publish posts',content_type=content_type,)
在使用python manage.py migrate
命令后,就会创建这个新权限,接着就可以在vIEw中编写代码判断用户是否有这个权限来决定能否发表文章。
可以在管理后台对用户授权:
或者把用户分组后,按组来进行授权:
从数据库这6张表就能看出来,有用户表、分组表、权限表,以及它们的关联关系表:
其代码实现是把permission赋值给User.user_permissions或者Group.permissions属性。
@H_156_301@代理模型权限代理模型是从某个模型继承来的,不影响表结构,用于扩展行为实现代码解耦。
代理模型不会继承父类的权限,例如:
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')]>>> # 注意代理模型取ContentType需要加for_concrete_model=False>>> 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'] # 没有父类的can_eat_pizzas权限
Session认证Django认证系统是基于Session的。Django把Web请求封装成了request(httpRequest类),然后通过中间件设置了session相关的属性:request.session、request.site、request.user。其中request.user就代表当前用户,如果未登陆它的值是AnonymousUser(匿名用户)的实例,如果已登陆它的值是User的实例。可以通过is_authenticated来判断是否已认证:
if request.user.is_authenticated: # Do something for authenticated users. ...else: # Do something for anonymous users. ...
用户登录我们先简单回顾一下基于session的登录过程:
Django提供了login()
函数来登录,把用户凭证保存到session中。它的函数签名如下:
login(request,user,backend=None)
示例:
import Jsonfrom django.contrib.auth import authenticate,loginfrom django.http import httpResponse# Create your vIEws here.def my_vIEw(request): request_body = Json.loads(request.body) username = request_body["username"] password = request_body["password"] user = authenticate(request,username=username,password=password) if user is not None: login(request,user) return httpResponse("logged in") else: return httpResponse("invalID login")
除了保存用户凭证,Django还会把认证后端也保存到session中,便于相同的认证后端下次可以直接获取到用户信息。至于保存哪个认证后端,Django按以下顺序选取:
使用login()
函数的backend参数值,如果赋值了的话。使用user.backend的值,如果有的话。使用settings中AUTHENTICATION_BACKENDS
的值,默认 ['django.contrib.auth.backends.ModelBackend']
。否则抛出异常。用户登出Django提供了@R_404_6192@ut()
函数来登出。它的函数签名如下:
@R_404_6192@ut(request)
示例:
from django.contrib.auth import @R_404_6192@utdef @R_404_6192@ut_vIEw(request): @R_404_6192@ut(request) # Redirect to a success page.
登出后session会被销毁,所有数据都会被清除,以防止其他人使用相同的浏览器再次登录后获取到之前用户的session数据。
login_required对于未登陆的用户,需要进行限制,必须先登陆才能进行访问。
传统方法
使用request.user.is_authenticated判断,然后重定向到登录页面:
from django.conf import settingsfrom django.shortcuts import redirectdef my_vIEw(request): if not request.user.is_authenticated: return redirect('%s?next=%s' % (settings.LOGIN_URL,request.path)) # ...
或者错误页面:
from django.shortcuts import renderdef my_vIEw(request): if not request.user.is_authenticated: return render(request,'myapp/login_error.HTML') # ...
login_required装饰器
login_required(redirect_fIEld_name='next',login_url=None)
示例:
from django.contrib.auth.decorators import login_required@login_requireddef my_vIEw(request): ...
它的处理是这样的:
如果用户没有登录,就重定向到settings.LOGIN_URL(默认值/accounts/login/
),同时把当前的绝对路径添加到查询字符串中,如:/accounts/login/?next=/polls/3/
。
如果用户已经登录了,正常执行vIEw代码。
login_required
的redirect_fIEld_name
参数是指登陆认证成功后重定向的页面,默认保存在叫做next
的查询字符串参数中(如/accounts/login/?next=/polls/3/)。可以修改为自定义:
from django.contrib.auth.decorators import login_required@login_required(redirect_fIEld_name='my_redirect_fIEld')def my_vIEw(request): ...
不过修改后还需要同时修改login模板等。
login_required
的login_url
参数是指登录页面的url,可以自定义,默认是/accounts/login/
,需要在URLconf中关联登陆视图:
from django.contrib.auth import vIEws as auth_vIEwspath('accounts/login/',auth_vIEws.LoginVIEw.as_vIEw()),
function vIEws和class-based vIEwsfunction vIEws(函数视图),视图是个函数:
from django.http import httpResponsedef my_vIEw(request): if request.method == 'GET': # <vIEw logic> return httpResponse('result')
class-based vIEws(基于类的视图),视图是个类:
from django.vIEws import VIEwclass MyVIEw(VIEw): def get(self,request): # <vIEw logic> return httpResponse('result')
为什么需要cbv?因为类可以继承,提高代码复用。由于Django的URLconf只能接受函数,所以cbv有个as_vIEw()方法用来返回一个函数:
# urls.pyfrom django.urls import pathfrom myapp.vIEws import MyVIEwurlpatterns = [ path('about/',MyVIEw.as_vIEw()),]
LoginrequiredMixinMixin是为了代码复用,从多个父类继承而来的类。如果使用的是class-based vIEws,那么可以使用LoginrequiredMixin,来实现login_required的效果,例如:
from django.contrib.auth.mixins import LoginrequiredMixinclass MyVIEw(LoginrequiredMixin,VIEw): login_url = '/login/' redirect_fIEld_name = 'redirect_to'
permission_required除了需要登录,有些视图还需要权限。Django提供了permission_required
装饰器,它的函数签名如下:
permission_required(perm,login_url=None,raise_exception=False)
示例:
from django.contrib.auth.decorators import permission_required@permission_required('polls.add_choice')def my_vIEw(request): ...
permission_required
的perm
参数,指的是权限,可以是单个权限,也可以是权限列表。
permission_required
的login_url
参数和login_required
的login_url
作用一样。
permission_required
的raise_exception
参数,可以用来抛出异常,赋值为True后会跳转到403(http ForbIDden)页面而非登录页面。
如果既想抛出异常 ,又想跳转到登录页面,那么可以同时添加这2个装饰器:
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如果使用的是class-based vIEws,那么可以使用PermissionrequiredMixin,来实现permission_required的效果,例如:
from django.contrib.auth.mixins import PermissionrequiredMixinclass MyVIEw(PermissionrequiredMixin,VIEw): permission_required = 'polls.add_choice' # Or multiple of permissions: permission_required = ('polls.vIEw_choice','polls.change_choice')
修改密码导致session失效登录成功后,Django会把加密后的密码hash值存入session中,每次请求时,会校验session中的密码和数据库中的密码是否匹配。如果修改了密码,数据库中的密码改变了,而session中的密码没有更新,那么密码就会匹配不上,导致session失效。django.contrib.auth的PasswordChangeVIEw和user_change_password视图会在修改密码时更新session中的密码hash,来避免session失效。如果对修改密码的视图进行了自定义,那么可以使用update_session_auth_hash(request,user)
来更新session中的密码,防止修改密码导致session失效。
Django提供了登录、登出、密码管理等视图。最简单的使用方式是在URLconf中配置:
urlpatterns = [ path('accounts/',include('django.contrib.auth.urls')),]
它会包含这些URL patterns:
accounts/login/ [name='login']accounts/@R_404_6192@ut/ [name='@R_404_6192@ut']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']
name是别名,可以使用reverse()函数来获取,如
reverse('login')
。
但有时我们需要自定义url,在URLconf中添加自定义url后,再加上相应视图即可,例如:
from django.contrib.auth import vIEws as auth_vIEwsurlpatterns = [ path('change-password/',auth_vIEws.PasswordChangeVIEw.as_vIEw()),]
所有的这些视图都是class-based vIEws,便于继承后重写进行自定义。
Django提供的相关视图有LoginVIEw、@R_404_6192@utVIEw、PasswordChangeVIEw、PasswordChangeDoneVIEw、PasswordresetVIEw、PasswordresetDoneVIEw、PasswordresetConfirmVIEw、PasswordresetCompleteVIEw。
快速上手体验如果想快速上手体验,可以按如下步骤进行 *** 作:
pip install django
,安装Django。
django-admin startproject project_name
,创建Django项目。
python manage.py migrate
,数据迁移,使用自带sqlite数据库即可。
python manage.py createsuperuser
,创建超级管理员。
python manage.py runserver
,启动项目。
访问http://127.0.0.1:8000/admin/
,用超管登录管理后台。
就可以使用Django自带认证系统了。
小结本文介绍了Django自带的基于session的认证系统,阐述了用户、组、认证与授权的相关概念,以及session认证的技术细节,最后讲解了如何快速上手体验的 *** 作步骤。虽然如今基于session认证用的很少了,但它却是理解Token、JWT认证的基础,仍然值得我们学习。
总结参考资料:
https://docs.djangoproject.com/en/3.1/topics/auth/
https://docs.djangoproject.com/en/3.1/topics/auth/default/
以上是内存溢出为你收集整理的Django认证系统并不鸡肋反而很重要全部内容,希望文章能够帮你解决Django认证系统并不鸡肋反而很重要所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)