Django 的认证和权限系统是怎么工作的?
简化版
Django 内置认证系统主要负责用户、登录、登出、密码哈希、Session 认证、权限和分组。请求经过 SessionMiddleware 与 AuthenticationMiddleware 后,会在 request.user 上得到当前用户;权限可以通过用户、组和 permission 进行判断。
详细版
Django 认证体系常见组件:
User模型:保存用户名、密码哈希、邮箱、状态等;- 密码哈希:不会明文保存密码;
- 登录登出:
login()、logout(); request.user:当前请求用户,未登录通常是 AnonymousUser;- Session:登录态通常保存在服务端 Session + 客户端 Cookie 标识中;
- Permission:基于模型的 add/change/delete/view 权限;
- Group:把权限分组后批量赋给用户;
- 装饰器和 mixin:例如
login_required、PermissionRequiredMixin。
面试时要说清楚:认证解决“你是谁”,权限解决“你能做什么”。二者相关但不是一回事。
完整版教学
一、认证和权限的边界
认证 Authentication 关注身份:
这个请求是谁发的?是否已经登录?
权限 Authorization 关注授权:
这个用户能不能访问这个资源或执行这个动作?
一个用户可以已经登录,但没有删除文章的权限;也可以未登录,只能访问公开页面。
二、登录态如何挂到 request.user
典型中间件顺序中有:
MIDDLEWARE = [
"django.contrib.sessions.middleware.SessionMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware",
]
SessionMiddleware 根据 Cookie 找到服务端 session 数据;AuthenticationMiddleware 再基于 session 里的用户 ID 加载用户,并挂到 request.user。
视图里可以这样判断:
def profile(request):
if request.user.is_authenticated:
...
未登录时,request.user 通常是 AnonymousUser,而不是 None。这是一个常见细节。
三、密码为什么不能明文保存
Django 保存的是密码哈希,不是原始密码。用户登录时,框架会把输入密码按对应算法处理后与存储值比较。
这有几个好处:
- 数据库泄露时,攻击者不能直接拿到明文密码;
- 可以配置更强的哈希算法;
- 支持密码哈希算法升级;
- 内置校验和密码重置流程更安全。
面试时不要只说“加密密码”。更准确的说法是“使用带盐的密码哈希”,因为哈希通常不可逆,而加密通常可逆。
四、权限系统怎么组织
Django 对每个模型通常会生成基础权限:
- add:增加;
- change:修改;
- delete:删除;
- view:查看。
可以判断用户是否有权限:
if request.user.has_perm("blog.change_article"):
...
Group 是权限集合。把权限赋给组,再把用户加入组,比逐个用户配置权限更好维护。
五、常见保护方式
函数视图:
from django.contrib.auth.decorators import login_required, permission_required
@login_required
def profile(request):
...
@permission_required("blog.change_article")
def edit_article(request):
...
类视图:
from django.contrib.auth.mixins import LoginRequiredMixin
from django.views.generic import TemplateView
class ProfileView(LoginRequiredMixin, TemplateView):
template_name = "profile.html"
这类封装比每个视图手写判断更统一。
六、自定义用户模型的注意点
Django 项目如果需要自定义用户模型,最好在项目初期就配置 AUTH_USER_MODEL。项目上线后再替换用户模型会牵涉外键、迁移和第三方应用,成本很高。
常见建议是:即使早期只用默认字段,也可以先继承 AbstractUser 建一个自定义用户模型,为以后扩展手机号、头像、组织等字段留空间。
七、常见误区
- 把认证和权限混为一谈。登录不代表拥有操作权限。
- 认为
request.user未登录时是None。通常是 AnonymousUser。 - 明文存密码或自己设计密码算法。应使用 Django 内置密码哈希体系。
- 上线后才想替换 User 模型,迁移成本很高。
- 只在前端隐藏按钮,不在后端校验权限。前端隐藏不是安全控制。
八、认证和权限在 DRF 中怎么对应
如果项目使用 Django REST Framework,认证和权限会进一步拆成 Authentication、Permission、Throttle 等组件。Authentication 负责识别“你是谁”,例如 SessionAuthentication、TokenAuthentication、JWTAuthentication;Permission 负责判断“你能不能访问”,例如 IsAuthenticated、IsAdminUser、自定义对象权限。
这和 Django 内置认证并不冲突。DRF 的认证类最终也会设置 request.user 和 request.auth,权限类再基于它们判断是否允许访问。面试时可以说:Django 内置 auth 提供用户、组、权限、登录态等基础能力,DRF 在 API 层提供更细粒度的认证和权限扩展点。
常见误区是把认证失败和权限失败混在一起。认证失败是系统无法确认用户身份,通常返回 401;权限失败是已经知道用户是谁,但不允许访问,通常返回 403。具体状态码还会受认证方案和框架配置影响,但概念上要区分。
九、安全实践:不要只会调用 login
真实项目里,认证权限系统还要考虑密码安全、会话安全和权限粒度。Django 默认使用安全的密码哈希机制,不应该明文保存密码;Session Cookie 应配置 HttpOnly、Secure、SameSite 等属性;后台敏感操作应结合 CSRF、防重放、审计日志。
权限粒度也要设计清楚。模型级权限适合控制“能不能增删改查某类对象”,对象级权限适合控制“能不能操作某一条数据”。例如用户可以修改自己的文章,但不能修改别人的文章,这就不是简单的全局 change_article 能完全表达的,需要在视图或权限类中结合对象所有者判断。
面试时可以按“认证、授权、会话、安全配置、对象权限”这条线回答。这样答案会从“Django 有 User 和 Permission”升级成完整的安全模型。
十、常见误区与追问
| 概念 | 回答的问题 | Django/DRF 中的体现 |
|---|---|---|
| 认证 | 你是谁 | login、session、Authentication |
| 权限 | 你能做什么 | Permission、Group、Permission class |
| 会话安全 | 身份如何持续可信 | Cookie 属性、CSRF、过期策略 |
- 误区:登录成功就代表有所有权限。 认证只确认身份,授权还要判断用户、组、模型权限或对象级权限。
- 误区:未登录时 request.user 是 None。 Django 通常给出
AnonymousUser,所以判断时应使用request.user.is_authenticated。 - 误区:前端隐藏按钮就完成权限控制。 后端视图、接口或权限类必须再次校验,否则直接请求接口仍可能越权。
- 追问:为什么密码不能自己加密保存? 应使用 Django 内置密码哈希体系,它包含盐、算法标识和可升级策略,比自定义方案可靠。
- 追问:什么时候需要自定义 User 模型? 需要手机号登录、组织字段、头像等扩展时应尽早配置
AUTH_USER_MODEL,上线后替换成本很高。 - 追问:DRF 中 401 和 403 怎么区分? 401 更偏认证失败或缺少有效凭证,403 更偏已识别身份但权限不足,具体表现受认证方案影响。
记忆钩子:认证回答“是谁”,权限回答“能不能”,会话安全回答“身份怎么安全地持续”;三层都讲到,答案才完整。
十一、加强记忆
Django 认证系统回答“用户是谁”,权限系统回答“用户能做什么”。SessionMiddleware 和 AuthenticationMiddleware 配合把用户挂到 request.user;密码保存用哈希;权限通过 user、group、permission 管理;后端必须做权限校验,不能只靠前端隐藏入口。