注意:后续技术分享,第一时间更新,以及更多更及时的技术资讯和学习技术资料,将在公众号CTO Plus发布,请关注公众号:CTO Plus
前面详细介绍了如何在DRF中使用权限以及如何自定义权限,实现了只有经过身份验证的用户可以创建文章并且只有文章的所有者可以编辑和删除文章。然而前篇文章中使用了Django默认的基于session的认证方式,实际前后端分离开发项目中后台更多采用的是token(令牌认证)。
本文将详细介绍认证(authentication)的本质,如何在DRF中使用自带的几种不同的认证方案,并重点介绍如何使用DRF自带的token认证。
JSON Web Token是一种更新的使用token进行身份认证的标准。与内置的TokenAuthentication方案不同,JWT身份验证不需要使用数据库来验证令牌, 而且可以轻松设置token失效期。Django中可以通过djangorestframework-simplejwt 这个第三方包轻松实现JWT认证,将在下篇文章中进行详细介绍。
认证(Authentication)
身份验证是将传入的请求对象(request)与一组标识凭据(例如请求来自用户或其签名的令牌token)相关联的机制。REST framework 提供了一些开箱即用的身份验证方案,并且还允许实现自定义方案。
DRF的每个认证方案实际上是一个类。可以在视图中使用一个或多个认证方案类。REST framework将尝试使用列表中的每个类进行身份验证,并使用成功完成验证的第一个类返回的元组设置 request.user 和request.auth。
用户通过认证后request.user返回Django的User实例,否则返回AnonymousUser的实例。request.auth通常为None。如果使用token认证,request.auth可以包含认证过的token。
注:认证一般发生在权限校验之前。
REST framework自带的认证方案
Django REST Framework提供了如下几种认证方案:
- Session认证SessionAuthentication类:此认证方案使用Django的默认session后端进行身份验证。当客户端发送登录请求通过验证后,Django通过session将用户信息存储在服务器中保持用户的请求状态。Session身份验证适用于与网站在相同的Session环境中运行的AJAX客户端 (注:这也是Session认证的最大弊端)。
- 基本认证BasicAuthentication类:此认证方案使用HTTP基本认证,针对用户的用户名和密码进行认证。使用这种方式后浏览器会跳出登录框让用户输入用户名和密码认证。基本认证通常只适用于测试。
- 远程认证RemoteUserAuthentication类:此认证方案为用户名不存在的用户自动创建用户实例。这个很少用,具体见文档。
- Token认证TokenAuthentication类:该认证方案是DRF提供的使用简单的基于Token的HTTP认证方案。当客户端发送登录请求时,服务器便会生成一个Token并将此Token返回给客户端,作为客户端进行请求的一个标识,以后客户端只需带上这个Token前来请求数据即可,无需再次带上用户名和密码。后面会详细介绍如何使用这种认证方案。
注意:如果在生产环境下使用BasicAuthentication和TokenAuthentication认证,必须确保API仅在https可用。
在DRF中使用自带认证方案
settings.py中设置默认的全局认证方案
# 设置默认的全局认证方案
# REST framework api的任何全局设置都保存 "REST_FRAMEWORK" 的配置中
REST_FRAMEWORK = {
# Use Django's standard `django.contrib.auth` permissions,
# or allow read-only access for unauthenticated users.
'DEFAULT_PERMISSION_CLASSES': [
# 'rest_framework.permissions.AllowAny', # 默认无限制访问
'rest_framework.authentication.BasicAuthentication',
'rest_framework.authentication.SessionAuthentication',
]
}
基于类的视图(CBV)中使用
函数视图中使用
自定义认证方案
要实现自定义的认证方案,要继承BaseAuthentication类并且重写.authenticate(self, request)方法。如果认证成功,该方法应返回(user, auth)的二元元组,否则返回None。
在某些情况下,可能不想返回None,而是希望从.authenticate()方法抛出AuthenticationFailed异常。
通常应该采取的方法是:
- 如果不尝试验证,返回None。还将检查任何其他正在使用的身份验证方案。
- 如果尝试验证但失败,则抛出AuthenticationFailed异常。无论任何权限检查,也不检查任何其他身份验证方案,立即返回错误响应。
也可以重写.authenticate_header(self, request)方法。如果实现该方法,则应返回一个字符串,该字符串将用作HTTP 401 Unauthorized响应中的WWW-Authenticate头的值。
如果.authenticate_header()方法未被重写,则认证方案将在未验证的请求被拒绝访问时返回HTTP 403 Forbidden响应。
自定义认证示例
以自定义请求标头中名称为'X_USERNAME'提供的用户名作为用户对任何传入请求进行身份验证,其它类似自定义认证需求,比如支持用户同时按用户名或email进行验证。
from django.contrib.auth.models import User, UserManager
1. User:Django 自带的用户模型,用于存储用户认证相关信息,如用户名、密码和电子邮件等。
2. UserManager:User 对象的管理器,用于管理数据库中的用户对象。它提供了一些方法,如 create_user() 和 create_superuser(),可以用于创建新用户或超级用户。
作用:
1. 管理用户:UserManager 可以帮助我们对 User 模型进行增、删、改、查的操作,以及实现用户注册、登录、身份验证等功能。
2. 扩展用户模型:在 Django 中,我们可以通过继承 AbstractBaseUser 或 AbstractUser 类来扩展自己的用户模型。而对于 User 对象的管理,则是使用 UserManager。
3. 自定义用户管理器:如果默认的 UserManager 无法满足需求,我们可以通过自定义 UserManager 类来实现更多定制化的管理功能。
from rest_framework import authentication
authentication.BaseAuthentication
authentication.BasicAuthentication
`authentication.BaseAuthentication` 是一个抽象基类,旨在提供认证的基本结构。它定义了 `authenticate()` 方法,用于实现自定义的认证逻辑,并返回认证后的用户对象和认证信息。它需要被子类化以实现具体的认证方式。例如,`TokenAuthentication` 和 `SessionAuthentication` 都是 `BaseAuthentication` 的子类,分别使用 token 和 session id 进行认证。
`authentication.BasicAuthentication` 是 `BaseAuthentication` 的子类,采用 HTTP 基本认证方式(即在每个请求的请求头中添加包含用户名和密码的 HTTP Authorization 头信息)来认证用户。它的工作流程如下:
1. 客户端使用 UTF-8 编码将用户名和密码连接成一个字符串 `username:password`。
2. 将字符串进行 base64 编码。
3. 客户端在每个请求的请求头中添加包含 base64 编码后的用户名和密码的 HTTP Authorization 头信息。
4. 服务端解码请求头信息,并使用解密后的用户名和密码进行查询,认证用户是否合法。
需要注意的是,由于用户名和密码以明文方式传输并以 base64 编码,因此并不安全,容易被中间人攻击拦截。如果需要更高级别的安全措施,建议使用其他的认证方式。
前后端分离时为何推荐token认证
前后端分离是指将前端与后端分离部署,使其成为独立的两个系统。在这种情况下,前端与后端进行通信时,需要采用一种机制来授权请求和响应,以保证安全性和可靠性。Token认证是一种比较适合前后端分离的认证方式,因为:
- 无状态:每个请求都携带着Token,后端可以通过Token进行认证和授权,而不需要保存用户的任何信息,从而保证了应用的无状态性,便于扩展。
- 安全性:Token认证在前端存储了用户的身份信息,后端只需要对Token进行验证即可,不需要对用户密码等敏感信息进行传输,因此可以有效降低安全风险。
- 可控性:Token认证可以通过设置Token的过期时间等控制Token的使用范围,在一段时间后失效,从而增加了应用的安全性。
- Token无需存储,降低服务器成本,session是将用户信息存储在服务器中的,当用户量增大时服务器的压力也会随着增大。
- 防御CSRF跨站伪造请求攻击,session是基于cookie进行用户识别的, cookie如果被截获,用户信息就容易泄露。
- 扩展性强,session需要存储无法共享,当搭建了多个服务器时其他服务器无法获取到session中的验证数据用户无法验证成功。而Token可以实现服务器间共享,这样不管哪里都可以访问到。
- Token可以减轻服务器的压力,减少频繁的查询数据库。
- 支持跨域访问,适用于移动平台应用。
因此,Token认证是前后端分离中一种安全性高、易扩展、易于管理的认证方式,值得推荐使用。
TokenAuthentication的使用
DRF自带的TokenAuthentication方案可以实现基本的token认证,整个流程如下:
首先,需要将修改settings.py, 加入如下app。
迁移migrate
查看生成的表
其次,需要为用户生成令牌(token)。如果希望在创建用户时自动生成token,可以借助Django的信号(signals)实现,如下所示:
如果已经创建了一些用户,则可以打开shell为所有现有用户生成令牌,如下所示:
Shell中导入包
循环遍历用户生成token
表中查看生成的token
还可以在admin.py中给用户创建token,如下所示:
从3.6.4起,还可以使用如下命令为一个指定用户新建或重置token。
- ./manage.py drf_create_token <username> # 新建
- ./manage.py drf_create_token -r <username> # 重置
接下来,需要暴露用户获取token的url地址(API接口).
每当用户使用form表单或JSON将有效的username和password字段POST提交到以上视图时,obtain_auth_token视图将返回如下JSON响应:
客户端拿到token后可以将其存储到本地cookie或localstorage里,下次发送请求时把token包含在Authorization HTTP头即可,如下所示:
- Authorization: Token a2e33be1f921eac15f3ecfc098159d6405dd24c4
通过curl工具来进行简单测试
- curl -X GET http://127.0.0.1:8000/api/example/ -H 'Authorization: Token a2e33be1f921eac15f3ecfc098159d6405dd24c4'
默认的obtain_auth_token视图返回的json响应数据是非常简单的,只有token一项。如果希望返回更多信息,比如用户id或email,就要通过继承ObtainAuthToken类定制这个视图,如下所示:
然后修改urls.py:
- urlpatterns +=[ path('api-token-auth/',CustomAuthToken.as_view())]
最后一步,DRF的TokenAuthentication类会从请求头中获取Token,验证其有效性。如果token有效,返回request.user。至此,整个token的签发和验证到此结束。
代码地址:https://download.csdn.net/download/zhouruifu2015/87652849
参考资料
Requests - Django REST framework
输入才有输出,吸收才能吐纳。——码字不易