Django 是 Python 语言中最受欢迎的 Web 框架之一。其开箱即用的特性,使得我们可以利用它快速搭建一个传统的 Web 应用。
在如今多端横行的互联网,单纯的传统 Web 应用开发已经越来越式微,更多的应用采用了前后端分离的 Web 开发模式,后端只是单纯地提供 API 给前端各个终端(Web、APP、小程序等)调用。
借助于 Django REST Framework 这个第三方库,Django 也能快速生成 RESTful 风格的 API 接口。
通常情况下,需要用户进行登录的 API,我们都统一使用 Token 来进行认证,这样可以确保接口对多端的支持。但是 Django 在 Web 网页端的功能实在是太好用了,以至于很多人舍不得放弃 Django 自带的认证功能。
如果让 Django 写的接口既支持 Token 认证,也能兼容 Django 自带的 Session 认证呢?DRF 框架本身就提供了支持。
DRF 支持的认证模式
REST framework 提供了许多开箱即用的身份认证方案,还允许自定义认证方案。
它一共提供了如下几种认证方案:
BasicAuthentication
(HTTP Basic 认证):用于根据用户名和密码进行 HTTP 基础身份认证。TokenAuthentication
(Token 认证):用于简单的基于 Token 的认证方案,这种方案适合于 CS 模式的应用。SessionAuthentication
(Session 认证):使用 Django 的默认会话后端进行身份验证。会话身份验证适用于与网站在相同的会话中运行的 AJAX 客户端。RemoteUserAuthentication
(远程用户分组):这种身份认证允许将身份认证交给另一个 Web 服务器(通过设置REMOTE_USER
变量指定认证服务器地址)
除此之外,我们还能自定义身份认证,只需要继承BaseAuthentication
类进行扩展即可。
在 DRF 中使用认证
在 DRF 框架中,可以通过 2 种方式配置认证方式。
一种是在 Django 的配置文件中通过 REST_FRAMEWORK
变量全局设置认证模式,例如:
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework.authentication.SessionAuthentication',
]
}
另一种则是在视图中通过authentication_classes
属性单独指定每一个视图的认证模式,例如:
from rest_framework.authentication import SessionAuthentication
from rest_framework.response import Response
from rest_framework.views import APIView
class ExampleView(APIView):
authentication_classes = [SessionAuthentication]
def get(self, request, format=None):
content = {
'user': str(request.user), # `django.contrib.auth.User` instance.
'auth': str(request.auth), # None
}
return Response(content)
使用多种认证模式
在上面的示例中,我们可以看到,DRF 的认证模式配置接收的是一个列表,其实我们可以在里面添加多种认证模式。例如:
代码语言:javascript复制from rest_framework.authentication import SessionAuthentication, BasicAuthentication
class ExampleView(APIView):
authentication_classes = [SessionAuthentication, BasicAuthentication]
这样,这个接口就可以通过两种方式进行身份认证。
需要特别注意的一点是,如果使用 Session 认证,那么在登录页面的时候,需要使用 Django 默认的登录视图进行登录操作。
同时,在 Web 页面进行接口请求的时候,需要在 headers 头里面带上X-CSRFToken
参数,其值为 Django 的 csrf_token
,例如:
headers: {"X-CSRFToken":'{{ csrf_token }}'},
多认证方式接口示例
在「觅道文档」中,我们就采用了这样的双认证方式来处理接口的认证。
例如,在用户列表接口中(/Mrdoc/app_admin/views.py 文件 283 行附近),我们是这样定义接口的:
代码语言:javascript复制# 后台管理 - 用户列表接口
class AdminUserList(APIView):
authentication_classes = [SessionAuthentication,AppMustAuth]
permission_classes = [SuperUserPermission]
# 获取用户列表
def get(self, request):
username = request.query_params.get('username', '')
page_num = request.query_params.get('page', 1)
limit = request.query_params.get('limit', 10)
if username == '':
user_data = User.objects.all().values(
'id', 'last_login', 'is_superuser', 'username', 'email', 'date_joined', 'is_active', 'first_name'
)
else:
user_data = User.objects.filter(username__icontains=username).values(
'id', 'last_login', 'is_superuser', 'username', 'email', 'date_joined', 'is_active', 'first_name'
)
page = PageNumberPagination() # 实例化一个分页器
page.page_size = limit
page_users = page.paginate_queryset(user_data, request, view=self) # 进行分页查询
serializer = UserSerializer(page_users, many=True) # 对分页后的结果进行序列化处理
resp = {
'code': 0,
'data': serializer.data,
'count': user_data.count()
}
return Response(resp)
这里面我们使用了SessionAuthentication
和AppMustAuth
这两个认证方式,其中AppMustAuth
是我们自定义的 Token 认证方式,其代码如下所示:
class AppMustAuth(BaseAuthentication):
'''自定义认证类'''
def authenticate(self, request):
token = request.query_params.get('token')
# print(token)
if token:
# 如果请求url中携带有token参数
user_obj = AppUserToken.objects.filter(token=token).first()
if user_obj:
# print("ok")
# token 是有效的,返回一个元组
return user_obj.user, token # request.user, request.auth
else:
raise AuthenticationFailed(_('无效的token'))
else:
raise AuthenticationFailed(_('请求的URL中必须携带token参数'))
如果我们在未登录或不带 Token 的情况下访问接口,会直接响应 403 Forbidden:
如果浏览器未登录状态下访问接口,会直接响应 403 Forbidden:
如果我们在浏览器登录状态下访问接口,会响应成功:
如果我们在接口中携带 Token 参数,也会响应成功:
有兴趣的小伙伴下载源码看一看、试一试。
?分享、点赞、在看,给个三连击呗!?