所有文章 > API设计 > 如何让 Python 写的 API 接口同时支持 Session 和 Token 认证?

如何让 Python 写的 API 接口同时支持 Session 和 Token 认证?

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 SessionAuthenticationfrom rest_framework.response import Responsefrom 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 的认证模式配置接收的是一个列表,其实我们可以在里面添加多种认证模式。例如:

from rest_framework.authentication import SessionAuthentication, BasicAuthenticationclass 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 行附近),我们是这样定义接口的:

# 后台管理 - 用户列表接口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)

这里面我们使用了SessionAuthenticationAppMustAuth这两个认证方式,其中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 参数,也会响应成功:


有兴趣的小伙伴下载源码看一看、试一试。

文章转自微信公众号@州的先生

#你可能也喜欢这些API文章!