上一节我们做了个简单的客户增删改查。这一节我们来看看怎么增加权限控制。毕竟赤裸裸的把客户信息放在网页上让所有人都能看到,肯定是不合适的。
基础知识(可忽略)
认证(Authentication):确认用户的身份,简单来说就是验证这个人是不是你(比如通过登录获得 token 或 session)。DRF 默认有两种认证方式:
- Token Authentication:基于一个独立的 token 来进行认证,通常适用于前后端分离的场景。
- 特点:
- 基于独立的Token:每次用户登陆后,系统会为用户生成唯一的Token。前端将这个Token保存到客户端,每次请求时,前端会将Token放在请求的Header中,后端通过这个Token来确认用户身份。
- 适用于前后端分离:Token 认证不依赖于浏览器的 cookie,因此它非常适合前后端分离的应用,前端可以独立与后端交互,而不需要依赖传统的会话管理机制。
- 跨域支持好:Token 可以通过 HTTP Header 传递,不受同源策略限制,所以即使前后端部署在不同域下,依然能够工作。
- 无状态认证:每次请求只需要 Token 即可,不需要服务器保存会话数据,减少了服务器的压力。不过Token 会暴露在请求头中,如果 Token 泄露,可能会带来安全风险,需要确保请求过程是加密的(比如使用 HTTPS)。Token 过期后需要重新登录或刷新 Token。
- 适用场景:
- 前后端分离的应用:比如 SPA(Single Page Application)或移动端应用。
- API 服务:需要提供对外服务的 API,比如第三方应用通过 API 访问你的数据。
- 跨平台登录:如果同一个 Token 可以在不同的客户端(移动端、Web)上复用,Token 认证可以非常方便地实现这一点。
- 如何工作:
- 用户通过用户名和密码登录,后端生成一个唯一的 Token 返回给前端。
- 前端将 Token 存储在
localStorage或sessionStorage中。 - 每次前端请求 API 时,都会把 Token 放在请求的 Header 中:
- 特点:
- Session Authentication:基于 Django 默认的 session 机制,通常与前端页面中的登录保持会话。
- 特点:
- 基于服务器会话:当用户登录时,Django 会在服务器端为用户创建一个会话(session),并返回一个会话 ID。这个会话 ID 会保存在用户浏览器的 cookie 中。每次用户发起请求时,浏览器会自动携带这个 cookie,后端通过会话 ID 来识别用户。
- 适用于传统的 Django + HTML 页面的应用:通常与前后端不分离的场景配合使用,后端和前端通常运行在同一个站点下,前端页面会直接操作后端的数据。
- 安全性:默认情况下,Session 会话 ID 存储在浏览器的 cookie 中,服务器端存储用户的会话数据,因此用户的身份信息不会暴露在前端。
- 如果应用与Django后端紧密集成,Session认证会很方便。但是如果是前后端分离,前端后端通常部署不同服务器,浏览器和服务器之间Cookie可能会遇到跨域问题。会话状态存在服务器端,扩展性差,高并发存储压力大。
- 适用场景:
- 传统 Web 应用:前端通过模板渲染(Django 的模板)与后端紧密耦合,用户的浏览器和服务器保持会话。
- 需要用户保持登录状态的应用:比如普通的登录网站,用户每次访问都会检查是否登录,且保持登录状态。
- 如何工作:
- 用户登录后,Django会为该用户创建一个Session,并返回Session ID,后续请求会自动带上这个ID。
- 认证通过后,可以使用IsAuthentivated或其他权限类来控制访问。
- 特点:
MD,傻逼GPT叽里咕噜说了一大堆,搞得复杂得很,又要装库又要搞setting配置。换了Gemini果然正常多了。下面直接看看代码,我们需要修改什么。
代码
当下很简单,我们在之前的view.py的类里加入一行代码,就这么简单,告诉程序必须是登录的用户才能访问。
permission_classes = [IsAuthenticated]
整体代码如下:
class CustomersSet(viewsets.ModelViewSet):
serializer_class = CustomerSerializer
filter_backends = [SearchFilter, OrderingFilter]
search_fields = ["name", "email", "phone", "address"]
ordering_fields = ["created_at", "updated_at", "name"]
# 启用 DRF 自带的过滤后端。
# SearchFilter:允许在 URL 上用 ?search=关键字 来搜索数据。
# OrderingFilter:允许在 URL 上用 ?ordering=字段名 来排序。
permission_classes = [IsAuthenticated] # 加上这行,给 API 上锁!
def get_queryset(self):
# 查询函数
return Customer.objects.all()
# return Customer.objects.filter(owner=self.request.user) # 只返回当前用户
def perform_create(self, serializer):
serializer.save()
# perform_create 是 DRF 在执行 create() 方法时会调用的钩子。
# 默认 serializer.save() 就会把数据存进数据库。
GPT之前叽里咕噜说了一大堆,其实就是在setting里修改。当我们期望这个登录认证是整个项目的默认行为时,就应该去修改 settings.py。DRF一般会默认两种认证方式。
DEFAULT_AUTHENTICATION_CLASSES (默认认证方式)
- rest_framework.authentication.SessionAuthentication:会自动使用 Django 的 session 体系。我们在做的登录功能时,正是把用户信息存入了session。所以,只要用户在浏览器里登录了,DRF 就能通过这个方式识别出他是谁 (request.user)。
- rest_framework.authentication.BasicAuthentication:这是一种简单的、基于 HTTP 头部的认证,通常用于开发或给其他程序调用 API 时,不适用于浏览器交互。
因为 SessionAuthentication 是默认启用的,所以 API 就认识 Django 的登录状态。
DEFAULT_PERMISSION_CLASSES (默认权限策略)
- rest_framework.permissions.AllowAny:DRF 的默认权限是允许任何人 (AllowAny) 访问。
这就是为什么在添加 permission_classes = [IsAuthenticated] 之前,任何人(无论登不登录)都能访问的客户 API。因为全局默认是不上锁。
OK,现在我们不登陆的话,访问的结果就是这样:

而登陆了,就正常了:
