6.2 校验器与 CSRF 防护


文档摘要

6.2 校验器与 CSRF 防护 本节摘要:isvalid 背后是一条分层校验链:字段类型转换、字段级校验器、字段 clean 方法、表单级 clean 方法。本节讲清四个挂载点的分工与自定义写法,再把 CSRF 防护的令牌闭环拆解一遍。校验写在正确的层,错误信息才既准确又可测。 校验链的四个挂载点 数据从 POST 进来到 cleaneddata 出去,要过四道关卡: 四层分工: 类型转换由字段类型自动完成,EmailField 天然校验格式; 字段级校验器是可复用的独立函数,同一个校验器能挂到多个表单甚至模型字段上; 字段 clean 方法适合"单字段、依赖清洗后其他字段"的规则; 表单级 clean 处理跨字段约束,错误可通过 adderror 指到具体字段或挂全表。

6.2 校验器与 CSRF 防护

本节摘要:is_valid 背后是一条分层校验链:字段类型转换、字段级校验器、字段 clean 方法、表单级 clean 方法。本节讲清四个挂载点的分工与自定义写法,再把 CSRF 防护的令牌闭环拆解一遍。校验写在正确的层,错误信息才既准确又可测。

校验链的四个挂载点

数据从 POST 进来到 cleaned_data 出去,要过四道关卡:

from django import forms from django.core.exceptions import ValidationError def forbidden_words(value): bad = ["广告", "刷单"] if any(w in value for w in bad): raise ValidationError("评论包含不允许的词汇。") class CommentForm(forms.Form): reviewer = forms.CharField(max_length=50) content = forms.CharField( max_length=2000, validators=[forbidden_words], # 挂载点二:字段级校验器 ) email = forms.EmailField(required=False) def clean_content(self): # 挂载点三:字段 clean 方法 data = self.cleaned_data["content"] if data.count("http") > 3: raise ValidationError("链接太多,疑似灌水。") return data # 记得返回,否则该字段丢失 def clean(self): # 挂载点四:表单级交叉校验 cleaned = super().clean() content = cleaned.get("content", "") reviewer = cleaned.get("reviewer", "") if reviewer in content: raise ValidationError("昵称不能出现在正文里。") return cleaned

四层分工:

  1. 类型转换由字段类型自动完成,EmailField 天然校验格式;
  2. 字段级校验器是可复用的独立函数,同一个校验器能挂到多个表单甚至模型字段上;
  3. 字段 clean 方法适合"单字段、依赖清洗后其他字段"的规则;
  4. 表单级 clean 处理跨字段约束,错误可通过 add_error 指到具体字段或挂全表。

选择层的标准:规则是否复用(复用做校验器)、是否跨字段(跨就进 clean)、是否伴随清洗(改数据用 clean 方法,校验器只判断不修改)。

校验器同样能挂到模型字段上,由模型层兜底——表单可能被绕过(第 8 章 Admin、第 10 章管理命令都能直接碰模型),模型层的校验器是最后防线。两层都挂的成本几乎为零,值得默认这么做。

⚠️ clean_字段 方法里两个高频错:忘记 return(字段悄悄消失)、直接读 self.cleaned_data 之外的原始 POST(绕过类型转换)。记住口诀"读净数据、还净数据"。

CSRF:表单安全的第一道锁

CSRF(跨站请求伪造)的攻击面:用户登录着墨迹博客,此时访问了恶意页面,该页面悄悄向博客发了一个 POST——浏览器会自动带上墨迹的会话 Cookie,攻击者借用户的登录态为所欲为。防法是让"表单页"与"提交"之间有一个攻击者拿不到的秘密。

Django 的闭环分三步,全部自动:

<form method="post"> {% csrf_token %} 第一步:模板标签渲染隐藏域,值为随机令牌 ... </form>
# 第二步:令牌同时写入用户会话(依赖 SessionMiddleware 已就绪) # 第三步:CsrfViewMiddleware 在视图前比对 请求里的令牌与会话中的令牌 # 不一致或缺失 → 403,请求根本到不了视图

不一致或缺失 → 403,请求根本到不了视图

这就是第 4 章中间件清单里 CsrfViewMiddleware 的日常。三个实践要点:

  • Ajax 提交也要带令牌:从 Cookie 读令牌放进请求头,前端框架配置一次即可。
  • 豁免要显式:个别接口确实无法带令牌(如第三方回调)时,用装饰器显式豁免并注释原因,绝不全局关闭。
  • 报错 403 却查不出原因时,先检查表单里是不是漏了 csrf_token 标签——它是最常见的 CSRF 失败原因。

错误信息的本地化与定制

错误信息在声明字段时可定制:

content = forms.CharField( max_length=2000, error_messages={"max_length": "评论太长了,最多 %(limit_value)d 字。", "required": "评论内容不能为空。"}, )

定制错误信息不是美化,是产品语气的一部分,也是第 9 章测试的断言对象——错误信息写清楚了,测试与用户文档都省力。

校验失败的信息设计

校验技术之外,还有一个直接影响产品质感的维度常被忽略:校验失败时用户看到什么。默认的错误提示是技术视角的该字段是必填项,但对用户来说,更友好的是直接指出问题所在并说明该怎么做——标题不能为空,请为文章起个名字。Django 表单体系对此有现成扩展点:字段的错误文案参数可以逐条定制各校验规则的提示,表单级校验抛出的异常信息也可以带上下文。墨迹博客上线初期的评论转化率偏低,排查后发现三成流失发生在表单环节——用户遇到生硬报错后直接离开。重写错误文案、把失败字段高亮、保留已填内容这三项改进上线后,转化率回升了明显一截。校验的完整使命是拦住坏数据,但拦下的方式决定了用户是留下来改正,还是带着挫败感离开。

本节要点回顾

  • 校验链四挂载点:类型转换、校验器、字段 clean、表单级 clean,按"复用与跨字段"选层。
  • 模型层校验器是最后防线,Admin 与脚本路径都绕不开它。
  • clean 方法口诀:读净数据、还净数据。
  • CSRF 令牌三步闭环:下发、携带、比对,失败挡在视图之前。

输入线打通,下一章处理"谁在输入"——认证、会话与权限。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U