资讯动态

Django请求处理链路实战:中间件、CSRF防护与auth认证协同机制详解

发布时间:2026/10/13 9:10:59 来源:尧图企业网站定制
1. 从请求进站到用户身份确认Django请求处理链路再梳理很多刚接触Django的朋友写视图函数写得挺顺手一旦遇到为什么我的请求还没进视图就被拦了为什么表单提交报403为什么登录状态莫名其妙丢了这类问题就开始抓瞎。这三个问题恰好对应Django里三个非常核心的机制中间件、跨站请求伪造防护、auth认证系统。它们一个管请求的进出通道一个管表单提交的安全校验一个管你是谁的身份确认。把这三块吃透Django项目里八成的玄学问题都能自己定位。这篇内容适合已经能跑通Django基础项目、但一遇到请求拦截、CSRF报错、登录态管理就卡壳的开发者。我会按照请求从进来到确认身份的真实链路来讲把中间件的执行顺序、CSRF的校验原理、auth的认证流程串成一条线而不是割裂地讲三个知识点。因为在实际项目里这三者本来就是协同工作的——中间件负责在请求到达视图前做统一处理CSRF防护在中间件层就介入了而auth认证则依赖中间件把用户对象挂到请求上。先建立一个整体认知一个HTTP请求进入Django后大致会经历WSGI处理器接收 → 中间件链的请求阶段 → URL路由匹配 → 视图处理 → 中间件链的响应阶段 → 返回响应这样一个流程。中间件就像一道道关卡请求进来时按注册顺序正序通过响应回去时按注册顺序倒序通过。这个正序进、倒序出的模型是理解一切中间件行为的基础后面讲执行顺序时会反复用到。而CSRF和auth本质上都是寄生在中间件体系里的功能模块。CSRF防护靠的是CsrfViewMiddleware这个中间件auth认证则依赖AuthenticationMiddleware把用户对象绑定到request.user上。所以你会发现想真正搞懂后两者绕不开对中间件机制的理解。这也是我把中间件放在最前面讲的原因——它是地基。2. 中间件请求流水线上的可插拔关卡2.1 中间件的本质与五种钩子中间件说白了就是一个Python类Django在请求处理的不同阶段会调用它定义好的特定方法。你可以把它想象成机场安检流程旅客请求从进入航站楼到登机到达视图中间要过好几道检查每道检查都可以决定放行、拦截或者给旅客贴个标签。Django的中间件提供了五个钩子方法分别对应请求处理的不同时机。最常用的两个是__init__和__call__。老版本的Django用process_request和process_response新版本1.10之后推荐用__call__配合get_response的写法。__init__只在服务器启动时执行一次用来做初始化比如读取配置、建立连接池。__call__则是每个请求都会走一遍是中间件的核心逻辑所在。除了这两个还有process_view、process_template_response、process_exception三个钩子。process_view在URL匹配成功、视图函数执行之前调用能拿到视图函数对象和参数process_exception在视图抛异常时触发可以用来做统一异常处理process_template_response则针对返回TemplateResponse的视图做后处理。这五个钩子覆盖了请求生命周期的关键节点用好了能省掉大量重复代码。我见过不少项目把日志记录、性能监控、权限预检、请求ID注入这些横切关注点全塞在视图里每个视图都写一遍维护起来极其痛苦。正确的做法就是抽成中间件。比如给每个请求生成一个唯一trace_id方便排查问题时串联日志这种需求用中间件实现只需要十几行代码而且对所有视图自动生效。2.2 执行顺序正序进、倒序出别记反了中间件的执行顺序是新手最容易搞混的地方。假设你在MIDDLEWARE配置里按A、B、C的顺序注册了三个中间件那么请求进来时的执行顺序是A → B → C响应返回时的顺序是C → B → A。这个规则一定要记牢因为它直接决定了你的逻辑该放在请求阶段还是响应阶段。举个实际例子。假设A中间件负责记录请求开始时间C中间件负责计算耗时。如果A在请求阶段记录开始时间那么它必须在响应阶段才能拿到结束时间并计算差值。由于响应阶段是倒序的C会先执行响应逻辑A最后执行。所以A的响应阶段代码能拿到完整的耗时数据这个设计是合理的。但如果你把耗时计算放在C的请求阶段那就完全错了因为那时候请求还没处理完。再比如做接口鉴权你希望鉴权中间件在业务逻辑之前执行那就应该把它注册在靠前的位置。而像GZip压缩这种对响应体做处理的中间件通常注册在靠后的位置因为响应阶段它是倒序执行的靠后注册意味着响应阶段靠前执行能尽早对响应体做压缩。理解了这个顺序模型配置中间件时就不会凭感觉乱放了。提示Django自带的SecurityMiddleware建议放在最前面GZipMiddleware建议放在靠后位置CsrfViewMiddleware和AuthenticationMiddleware的顺序也有讲究后面会具体说。2.3 手写一个请求耗时监控中间件光讲理论没意思直接上一个能用的中间件。下面这个中间件记录每个请求的耗时超过阈值的请求会打印警告日志方便定位慢接口。import time import logging logger logging.getLogger(__name__) class RequestTimingMiddleware: def __init__(self, get_response): self.get_response get_response self.slow_threshold 0.5 # 500毫秒 def __call__(self, request): start time.perf_counter() response self.get_response(request) duration time.perf_counter() - start response[X-Request-Duration] f{duration:.4f}s if duration self.slow_threshold: logger.warning( 慢请求: %s %s 耗时 %.4fs, request.method, request.path, duration ) return response这个中间件做了三件事记录开始时间、调用后续处理链、计算耗时并写入响应头。time.perf_counter()比time.time()精度更高适合做性能测量。把耗时写进响应头X-Request-Duration前端和运维都能直接看到排查问题时很方便。注册方式就是在settings.py的MIDDLEWARE列表里加上它的路径。注意__init__里只做一次性的初始化__call__里才是每个请求都执行的逻辑。这个区分很重要如果把耗时阈值读取放在__call__里每个请求都读一次配置纯属浪费。2.4 中间件里那些容易踩的坑第一个坑是在中间件里访问request.user。如果你在AuthenticationMiddleware之前注册的中间件里访问request.user会直接报错因为那时候用户对象还没绑定上去。解决办法是把需要用户信息的中间件注册在AuthenticationMiddleware之后。第二个坑是中间件里抛异常导致响应阶段被跳过。如果请求阶段的代码抛了异常Django会走异常处理流程响应阶段的中间件可能不会按预期执行。所以中间件里的代码要做好异常捕获尤其是那些做资源清理的逻辑最好放在try/finally里。第三个坑是中间件顺序影响CSRF校验。CsrfViewMiddleware必须在AuthenticationMiddleware之前还是之后实际上Django默认配置里CsrfViewMiddleware在AuthenticationMiddleware之前。这个顺序一般不用改但如果你自定义了认证相关的中间件就要注意别打乱这个默认顺序否则可能出现CSRF校验拿不到session的情况。第四个坑是异步视图下的中间件兼容性。Django 3.1之后支持异步视图但中间件默认是同步的。如果你的项目里混用了异步视图中间件需要额外处理否则会报SynchronousOnlyOperation错误。这个在升级Django版本时特别容易遇到。3. 跨站请求伪造403错误的来龙去脉与破解之道3.1 CSRF到底防的是什么跨站请求伪造CSRF攻击的原理其实不复杂。假设你登录了某个银行网站浏览器里存着你的登录Cookie。这时候你手贱点开了一个恶意网站那个网站里藏了一个自动提交的表单目标地址是银行网站的转账接口。由于你的浏览器会自动带上银行的Cookie这个请求在银行服务器看来就是你本人发起的转账就这么成功了。CSRF防护的核心思路就是光有Cookie不够还得有一个只有真正从你网站页面发起的请求才带得上的凭证。Django的做法是在Cookie里存一个随机token同时要求表单里也带上同一个token服务器比对两者是否一致。恶意网站虽然能让浏览器自动带上Cookie但它读不到Cookie里的token值受同源策略保护也就没法在表单里填上正确的token请求自然就被拒了。这个机制在Django里由CsrfViewMiddleware实现。它会在响应里设置csrftoken这个Cookie同时校验POST请求里的csrfmiddlewaretoken字段或者请求头X-CSRFToken是否和Cookie里的值匹配。不匹配就返回403这就是那个让人又爱又恨的CSRF verification failed错误的来源。3.2 模板里那个{% csrf_token %}到底干了啥在Django模板里写表单时几乎每个教程都会告诉你加一句{% csrf_token %}。但很多人加了就加了不知道它具体生成了什么。实际上这个模板标签会渲染出一个隐藏的input字段input typehidden namecsrfmiddlewaretoken value随机token值这个token值是从Cookie里取出来经过处理的。当表单提交时这个字段会跟着POST数据一起发到服务器CsrfViewMiddleware拿它和Cookie里的值做比对。两者一致才放行。这里有个细节值得注意token值不是简单地把Cookie值复制过来Django会对它做一次掩码处理mask。这样做的目的是防止BREACH攻击——一种通过压缩率推断内容的攻击方式。虽然日常开发不用关心这个细节但知道Django在安全上做了这层考虑能帮你理解为什么token看起来和Cookie里的值不一样。对于AJAX请求情况稍微不同。你需要在请求头里带上X-CSRFToken值从Cookie里读。Django官方推荐的做法是从Cookie里读csrftoken然后设置到请求头。下面是一个用原生fetch的示例function getCookie(name) { const value ; ${document.cookie}; const parts value.split(; ${name}); if (parts.length 2) return parts.pop().split(;).shift(); } fetch(/api/submit/, { method: POST, headers: { X-CSRFToken: getCookie(csrftoken), Content-Type: application/json, }, body: JSON.stringify({ data: test }) });3.3 什么时候可以合法地豁免CSRF校验有些场景下CSRF校验确实没必要比如纯API接口用Token认证而非Cookie认证或者某些第三方回调接口。Django提供了几种豁免方式但用之前一定要想清楚是否真的安全。最细粒度的方式是给单个视图加csrf_exempt装饰器from django.views.decorators.csrf import csrf_exempt csrf_exempt def webhook_receiver(request): # 处理第三方回调 pass这个装饰器告诉中间件这个视图不用校验CSRF。适合那种不依赖Cookie认证的接口比如用签名验证的支付回调。如果整个项目都用Token认证可以在中间件配置里直接去掉CsrfViewMiddleware。但这么做等于全局关闭防护风险很大除非你确定所有接口都不依赖Cookie。更稳妥的做法是保留中间件只对特定视图豁免。还有一种情况是前后端分离项目前端域名和后端域名不同。这时候Cookie的SameSite属性设置就很关键。Django 3.1之后支持通过CSRF_COOKIE_SAMESITE配置来控制默认是Lax。如果前后端跨域可能需要设置成None并配合Secure仅HTTPS同时后端要正确配置CORS。这块配置不对就会出现明明带了token还是403的情况。3.4 排查403的完整思路链路遇到CSRF 403错误别急着重启服务按下面这个链路一步步排查基本都能定位到原因。第一步看浏览器开发者工具的Network面板找到那个403的请求检查请求头里有没有X-CSRFToken或者表单数据里有没有csrfmiddlewaretoken字段。如果没有说明前端根本没带上token问题出在前端。第二步检查Cookie里有没有csrftoken。如果没有说明服务器没下发这个Cookie。可能的原因是视图用了csrf_exempt或者CSRF_COOKIE_HTTPONLY配置成了True导致JS读不到但表单提交不受影响。还有一种可能是Cookie的Domain或Path设置不对导致浏览器没存。第三步如果token和Cookie都有但还是403那就比对两者的值。注意Django会对token做掩码所以直接比对字符串肯定不一样。这时候要看的是Django的日志它会打印具体的失败原因比如CSRF token missing还是CSRF token incorrect。第四步检查中间件顺序。如果CsrfViewMiddleware被放在了AuthenticationMiddleware后面而你的认证逻辑又依赖session可能出现session还没建立就校验CSRF的情况。默认配置里CsrfViewMiddleware在AuthenticationMiddleware之前一般不用动。第五步检查是不是跨域问题。跨域请求下浏览器可能不发送Cookie或者发送了但后端因为CORS配置没接收。这时候要看响应头里有没有正确的Access-Control-Allow-Credentials和Access-Control-Allow-Origin。注意CSRF_COOKIE_HTTPONLY默认是False这样JS才能读取Cookie里的token用于AJAX请求。如果你把它设成TrueAJAX请求就得换一种方式获取token比如从页面meta标签里读。4. auth认证从request.user到完整用户体系4.1 AuthenticationMiddleware如何把用户挂到请求上每次请求进来AuthenticationMiddleware都会做一件事从session里读取用户ID然后去数据库查出对应的用户对象挂到request.user上。如果session里没有用户ID或者查不到对应用户request.user就是一个AnonymousUser匿名用户对象。这个机制的关键在于session。用户登录成功后Django会把用户ID写进sessionsession数据存在数据库或缓存里浏览器只保存一个sessionid的Cookie。下次请求带着这个Cookie来中间件就能根据sessionid找到对应的session数据进而找到用户。这里有个性能细节值得注意每个请求都会查一次用户表。如果用户表很大或者请求量很高这个查询会成为瓶颈。优化方式是用select_related预加载关联对象或者用缓存层缓存用户对象。Django本身也提供了AUTHENTICATION_BACKENDS配置可以自定义认证逻辑比如从LDAP或第三方系统认证。request.user对象上最常用的几个属性是is_authenticated是否已认证、is_staff是否能进admin、is_superuser是否超级用户。判断用户是否登录标准写法是if request.user.is_authenticated:注意这是个属性不是方法别加括号。4.2 登录、登出、权限校验的标准写法Django的auth系统提供了一套开箱即用的视图函数但实际项目里我们更多是用它的底层API自己写视图。登录的核心是authenticate和login两个函数from django.contrib.auth import authenticate, login, logout from django.http import JsonResponse def login_view(request): if request.method POST: username request.POST.get(username) password request.POST.get(password) user authenticate(request, usernameusername, passwordpassword) if user is not None: login(request, user) return JsonResponse({code: 0, msg: 登录成功}) return JsonResponse({code: 1, msg: 用户名或密码错误}) return JsonResponse({code: 1, msg: 方法不允许})authenticate负责验证用户名密码验证通过返回用户对象失败返回None。login负责把用户ID写进session。注意authenticate的第一个参数是request这是Django 1.11之后的要求为了支持自定义认证后端。登出就简单了调用logout(request)即可它会清空当前session。权限校验用装饰器最方便from django.contrib.auth.decorators import login_required, permission_required login_required def profile(request): return JsonResponse({username: request.user.username}) permission_required(app.change_article, raise_exceptionTrue) def edit_article(request, article_id): passlogin_required会把未登录用户重定向到登录页或者返回403取决于配置。permission_required检查用户是否有特定权限raise_exceptionTrue表示没权限时直接抛403而不是重定向。4.3 自定义用户模型什么时候该动手Django自带的User模型有username、password、email、first_name、last_name这几个字段。如果你的项目需要手机号登录、需要用户头像、需要昵称那就该考虑自定义用户模型了。这里有一条铁律自定义用户模型必须在项目第一次迁移之前做。如果已经跑过migrate再改AUTH_USER_MODEL会非常麻烦基本等于重建数据库。所以新项目启动时先想清楚用户模型要不要定制。自定义的方式有两种。一种是继承AbstractUser保留原有字段额外加字段from django.contrib.auth.models import AbstractUser from django.db import models class CustomUser(AbstractUser): phone models.CharField(max_length11, uniqueTrue, blankTrue) avatar models.URLField(blankTrue) nickname models.CharField(max_length30, blankTrue) class Meta: db_table custom_user然后在settings.py里设置AUTH_USER_MODEL app.CustomUser。这样Django的auth系统就会用你的模型request.user也会是CustomUser实例。另一种是继承AbstractBaseUser完全自定义字段和认证逻辑。这种方式灵活但工作量大需要自己实现用户管理器和认证后端。除非有特殊需求一般用AbstractUser就够了。4.4 认证后端的扩展与多方式登录Django默认的认证后端是ModelBackend它用username和password做认证。如果你想支持手机号登录、邮箱登录或者对接第三方认证就需要自定义认证后端。from django.contrib.auth.backends import ModelBackend from django.contrib.auth import get_user_model from django.db.models import Q UserModel get_user_model() class MultiFieldAuthBackend(ModelBackend): def authenticate(self, request, usernameNone, passwordNone, **kwargs): try: user UserModel.objects.get( Q(usernameusername) | Q(phoneusername) | Q(emailusername) ) except UserModel.DoesNotExist: return None if user.check_password(password) and self.user_can_authenticate(user): return user return None这个后端支持用用户名、手机号或邮箱登录。注册到AUTHENTICATION_BACKENDS后authenticate会依次尝试每个后端只要有一个返回用户对象就算认证成功。这里有个容易忽略的点user_can_authenticate会检查is_active字段。如果用户被禁用is_activeFalse即使密码正确也认证不通过。这个检查很重要别自己实现认证逻辑时漏掉了。多个后端并存时顺序很重要。Django会按配置顺序依次尝试一旦某个后端返回了用户对象就停止。所以把最常用的后端放前面能减少不必要的查询。5. 三者协同一个完整请求的实战拆解5.1 从登录到提交表单的完整链路现在把中间件、CSRF、auth串起来看一个真实场景用户登录后提交一篇文章。这个场景涉及登录态校验、CSRF校验、权限检查正好覆盖三个机制。请求进入Django后首先经过SecurityMiddleware做安全头处理然后SessionMiddleware解析sessionid找到session数据接着CsrfViewMiddleware准备CSRF校验再然后AuthenticationMiddleware根据session里的用户ID查出用户对象挂到request.user上。请求到达视图后login_required装饰器检查request.user.is_authenticated未登录直接重定向。视图处理POST数据时CsrfViewMiddleware已经在请求阶段校验过CSRF token了不合法的话根本到不了视图。如果视图还加了permission_required还会检查用户权限。响应返回时中间件倒序执行。CsrfViewMiddleware会在响应里设置csrftokenCookie如果还没有的话SessionMiddleware会保存session变更最后SecurityMiddleware添加安全响应头。这个链路里任何一环出问题都会导致请求失败。比如session过期了request.user变成匿名用户login_required就会拦截CSRF token没带对CsrfViewMiddleware直接返回403用户没有对应权限permission_required抛异常。5.2 常见组合问题的排查对照表实际开发中遇到的问题往往是多个机制交织的下面这张表整理了常见现象和对应的排查方向。现象可能原因排查方向POST请求403提示CSRFtoken未带或Cookie未下发检查表单/请求头、Cookie、中间件顺序登录后仍显示未登录session未保存或Cookie被拦检查SESSION_COOKIE配置、浏览器CookieAJAX请求403但表单正常请求头未带X-CSRFToken检查JS读取Cookie逻辑、CSRF_COOKIE_HTTPONLY中间件里访问user报错中间件顺序在auth之前调整MIDDLEWARE顺序跨域请求Cookie丢失SameSite或CORS配置检查CSRF_COOKIE_SAMESITE、CORS头自定义用户模型迁移报错迁移前未设置AUTH_USER_MODEL新库重建或手动处理依赖这张表建议收藏遇到问题先对号入座能省不少排查时间。5.3 生产环境下的配置清单开发环境跑通不代表生产环境没问题。下面这些配置项在上线前一定要检查一遍。CSRF_COOKIE_SECURE True确保CSRF Cookie只通过HTTPS传输。SESSION_COOKIE_SECURE True同理。CSRF_COOKIE_HTTPONLY保持False否则AJAX拿不到token。SESSION_COOKIE_HTTPONLY True防止JS读取sessionid这个和CSRF的配置是相反的别搞混。CSRF_TRUSTED_ORIGINS要配置上你的正式域名Django 4.0之后这个配置是必须的否则跨域请求会被拒。SECURE_PROXY_SSL_HEADER如果用了反向代理要配置否则Django识别不出HTTPS。AUTH_PASSWORD_VALIDATORS别用默认的空配置至少加上最小长度和常见密码检查。SESSION_COOKIE_AGE根据业务设置合理的过期时间默认两周金融类应用可能要求更短。AUTHENTICATION_BACKENDS如果自定义了确保包含ModelBackend或者你自己的完整实现别漏了user_can_authenticate检查。5.4 我踩过的几个真实坑第一个坑是关于csrf_exempt和login_required的装饰器顺序。如果写成csrf_exempt在login_required上面CSRF豁免会生效但登录检查也会执行。如果顺序反了可能CSRF校验先执行导致403根本到不了登录检查。装饰器是从下往上应用的写的时候要留意。第二个坑是自定义中间件里修改了request.user。有次我想在中间件里做单点登录直接给request.user赋了新值。结果发现视图里拿到的还是原来的用户。原因是AuthenticationMiddleware在响应阶段会把request.user重置回去。正确做法是用login()函数或者直接操作session。第三个坑是CSRF token在页面缓存后失效。有次给页面加了缓存结果用户打开缓存的页面提交表单一直403。原因是缓存页面里的CSRF token是旧的和当前Cookie里的不匹配。解决办法是用never_cache装饰器或者模板片段缓存时排除CSRF token部分。第四个坑是session和CSRF的存储冲突。有次把session存在Redis里CSRF也配置了缓存结果两者key冲突导致校验失败。后来把CSRF的缓存key前缀单独配置才解决。这个坑比较隐蔽排查了很久。6. 进阶把三个机制用出花来6.1 用中间件做统一的接口签名校验CSRF防护的是浏览器场景但如果是开放API通常用签名机制。可以写一个中间件统一做签名校验把签名逻辑从每个视图里抽出来。import hashlib import time class APISignatureMiddleware: def __init__(self, get_response): self.get_response get_response def __call__(self, request): if request.path.startswith(/api/): sign request.headers.get(X-Signature) timestamp request.headers.get(X-Timestamp) if not sign or not timestamp: return JsonResponse({code: 401, msg: 缺少签名}) if abs(time.time() - int(timestamp)) 300: return JsonResponse({code: 401, msg: 签名过期}) # 根据业务规则计算签名并比对 expected self._calc_sign(request, timestamp) if sign ! expected: return JsonResponse({code: 401, msg: 签名错误}) return self.get_response(request)这个中间件对所有/api/开头的请求做签名校验校验不通过直接返回不会进到视图。签名算法里通常会包含请求路径、参数、时间戳和密钥具体规则根据业务定。时间戳校验是为了防止重放攻击一般允许5分钟误差。6.2 基于auth的细粒度权限控制Django自带的权限是模型级别的add/change/delete/view但实际业务往往需要更细的控制比如只能编辑自己发的文章。这种需求用user_passes_test装饰器或者自定义权限类来实现。from django.contrib.auth.decorators import user_passes_test def is_author(user, article): return article.author user def author_required(view_func): def wrapper(request, *args, **kwargs): article_id kwargs.get(article_id) article get_object_or_404(Article, idarticle_id) if not is_author(request.user, article): return JsonResponse({code: 403, msg: 无权操作}) return view_func(request, *args, **kwargs) return wrapper这种自定义装饰器比通用权限更灵活能拿到视图参数做判断。如果项目里这类需求多可以考虑用Django Guardian这类第三方库做对象级权限但引入依赖前要评估维护成本。6.3 无状态认证与session的取舍传统session认证在分布式部署时需要考虑session共享问题通常用Redis集中存储。但有些场景更适合无状态认证比如移动端API、微服务间调用。Django本身不内置JWT但可以用第三方库实现。选择哪种方式核心看几个因素是否需要服务端主动注销用户session可以JWT需要黑名单机制、是否有跨域需求JWT放请求头更灵活、系统规模小系统session够用大系统JWT扩展性更好。没有绝对优劣只有适不适合。如果继续用session记得配置SESSION_ENGINE为缓存后端别用默认的数据库存储否则每次请求都查库性能扛不住。缓存后端用Redis的话SESSION_ENGINE django.contrib.sessions.backends.cache配合SESSION_CACHE_ALIAS指定缓存实例。6.4 安全加固的几个额外建议除了CSRF和auth还有几个安全配置容易被忽略。SECURE_HSTS_SECONDS开启HSTS强制浏览器用HTTPS访问。SECURE_CONTENT_TYPE_NOSNIFF True防止MIME类型嗅探。X_FRAME_OPTIONS DENY防止页面被嵌入iframe避免点击劫持。密码存储方面Django默认用PBKDF2算法迭代次数可配置。如果对安全要求高可以换成Argon2需要安装argon2-cffi并配置PASSWORD_HASHERS。不过换算法后旧密码需要重新哈希迁移时要考虑兼容。日志方面认证失败、CSRF失败、权限拒绝这些事件都应该记录日志方便安全审计。Django的django.securitylogger会记录这些事件确保它没有被过滤掉。生产环境日志级别至少INFO别设成ERROR把安全事件漏了。最后提醒一句安全配置没有一劳永逸的Django版本升级时经常会有安全相关的默认值变更。每次升级后花十分钟看看release notes里的安全部分比出事后再补救划算得多。我在实际项目里养成的习惯是每次部署前跑一遍python manage.py check --deploy它会提示哪些安全配置没到位虽然不能覆盖所有问题但能挡住大部分低级错误。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑