中间件

本文档解释了 Django 附带的所有中间件组件。有关如何使用它们以及如何编写自己的中间件的信息,请参阅中间件使用指南

可用中间件

缓存中间件

class UpdateCacheMiddleware[source]
class FetchFromCacheMiddleware[source]

启用站点范围的缓存。如果启用这些功能,则每个 Django 提供的页面都将被缓存,缓存时间由CACHE_MIDDLEWARE_SECONDS设置定义。请参阅缓存文档

“通用”中间件

class CommonMiddleware[source]
response_redirect_class

默认为HttpResponsePermanentRedirect。子类化CommonMiddleware并覆盖该属性以自定义中间件发出的重定向。

为完美主义者添加了一些便利功能

  • 禁止访问DISALLOWED_USER_AGENTS设置中列出的用户代理,该设置应为已编译正则表达式对象的列表。

  • 根据APPEND_SLASHPREPEND_WWW设置执行 URL 重写。

    如果APPEND_SLASHTrue且初始 URL 结尾没有斜杠,并且在 URLconf 中找不到,则通过在末尾追加斜杠来形成一个新 URL。如果此新 URL 在 URLconf 中找到,则 Django 将请求重定向到此新 URL。否则,将按通常方式处理初始 URL。

    例如,如果foo.com/bar没有有效的 URL 模式,但foo.com/bar/有有效的模式,则foo.com/bar将重定向到foo.com/bar/

    如果PREPEND_WWWTrue,则缺少前导“www.”的 URL 将重定向到具有前导“www.”的相同 URL。

    这两个选项都旨在规范化 URL。其理念是每个 URL 应该存在于一个且仅一个位置。从技术上讲,URLfoo.com/barfoo.com/bar/不同——搜索引擎索引器会将其视为不同的 URL——因此最佳实践是规范化 URL。

    如有必要,可以使用no_append_slash()装饰器将各个视图排除在APPEND_SLASH行为之外

    from django.views.decorators.common import no_append_slash
    
    
    @no_append_slash
    def sensitive_fbv(request, *args, **kwargs):
        """View to be excluded from APPEND_SLASH."""
        return HttpResponse()
    
  • 为非流式响应设置Content-Length标头。

class BrokenLinkEmailsMiddleware[source]

GZip 中间件

class GZipMiddleware[source]
max_random_bytes

默认为 100。子类化GZipMiddleware并覆盖该属性以更改压缩响应中包含的最大随机字节数。

注意

安全研究人员发现,当在网站上使用压缩技术(包括GZipMiddleware)时,该网站可能会面临多种可能的攻击。

为了减轻攻击,Django 实施了一种称为Heal The Breach (HTB)的技术。它向每个响应添加最多 100 个字节(请参阅max_random_bytes)的随机字节,以降低攻击的有效性。

有关更多详细信息,请参阅BREACH 论文 (PDF)breachattack.comHeal The Breach (HTB) 论文

django.middleware.gzip.GZipMiddleware为理解 GZip 压缩(所有现代浏览器)的浏览器压缩内容。

此中间件应放置在需要读取或写入响应主体的所有其他中间件之前,以便随后进行压缩。

如果满足以下任何条件,则不会压缩内容

  • 内容主体长度小于 200 字节。

  • 响应已设置Content-Encoding标头。

  • 请求(浏览器)未发送包含gzipAccept-Encoding标头。

如果响应具有ETag标头,则 ETag 将变为弱 ETag 以符合RFC 9110 第 8.8.1 节

可以使用gzip_page()装饰器将 GZip 压缩应用于各个视图。

条件 GET 中间件

class ConditionalGetMiddleware[source]

处理条件 GET 操作。如果响应没有ETag标头,则中间件将根据需要添加一个。如果响应具有ETagLast-Modified标头,并且请求具有If-None-MatchIf-Modified-Since,则响应将替换为HttpResponseNotModified

区域设置中间件

class LocaleMiddleware[source]
response_redirect_class

默认为 HttpResponseRedirect。子类化 LocaleMiddleware 并覆盖该属性以自定义中间件发出的重定向。

根据请求中的数据启用语言选择。它为每个用户定制内容。请参阅 国际化文档

消息中间件

class MessageMiddleware[source]

启用基于 Cookie 和会话的消息支持。请参阅 消息文档

安全中间件

警告

如果您的部署环境允许,通常最好让您的前端 Web 服务器执行 SecurityMiddleware 提供的功能。这样,如果有一些请求不是由 Django 提供服务(例如静态媒体或用户上传的文件),它们将与 Django 应用程序的请求具有相同的保护。

class SecurityMiddleware[source]

django.middleware.security.SecurityMiddleware 为请求/响应周期提供了一些安全增强功能。每个功能都可以通过设置独立启用或禁用。

HTTP 严格传输安全

对于应该只通过 HTTPS 访问的网站,您可以指示现代浏览器拒绝通过不安全的连接(在给定时间段内)连接到您的域名,方法是设置 “Strict-Transport-Security” 标头。这减少了您暴露于某些 SSL 剥离中间人 (MITM) 攻击的风险。

SecurityMiddleware 将在所有 HTTPS 响应中为您设置此标头,前提是您将 SECURE_HSTS_SECONDS 设置设置为非零整数值。

启用 HSTS 时,最好先使用较小的值进行测试,例如,SECURE_HSTS_SECONDS = 3600(一小时)。每次 Web 浏览器从您的网站看到 HSTS 标头时,它都会拒绝以不安全的方式(使用 HTTP)与您的域名通信给定的时间段。一旦您确认所有资产都在您的网站上安全地提供服务(即 HSTS 没有破坏任何东西),最好增加此值,以便不常访问的访客也能得到保护(31536000 秒,即 1 年,很常见)。

此外,如果您将 SECURE_HSTS_INCLUDE_SUBDOMAINS 设置设置为 TrueSecurityMiddleware 将向 Strict-Transport-Security 标头添加 includeSubDomains 指令。建议这样做(假设所有子域都只使用 HTTPS 提供服务),否则您的网站可能仍然容易受到通过子域的不安全连接的攻击。

如果您希望将您的网站提交到 浏览器预加载列表,请将 SECURE_HSTS_PRELOAD 设置设置为 True。这会将 preload 指令附加到 Strict-Transport-Security 标头。

警告

HSTS 策略适用于您的整个域名,而不仅仅是您在其上设置标头的响应的 URL。因此,只有在您的整个域名都只通过 HTTPS 提供服务时,您才应该使用它。

正确遵守 HSTS 标头的浏览器将拒绝允许用户绕过警告并连接到具有已过期、自签名或其他无效 SSL 证书的站点。如果您使用 HSTS,请确保您的证书处于良好状态并保持这种状态!

注意

如果您部署在负载均衡器或反向代理服务器后面,并且 Strict-Transport-Security 标头未添加到您的响应中,这可能是因为 Django 没有意识到它处于安全连接中;您可能需要设置 SECURE_PROXY_SSL_HEADER 设置。

推荐人策略

浏览器使用 Referer 标头 作为一种向站点发送有关用户如何到达该站点的信息的方式。当用户点击链接时,浏览器会将链接页面的完整 URL 作为推荐人发送。虽然这在某些情况下很有用——例如,了解谁链接到您的网站——但它也会因告知一个网站用户正在访问另一个网站而导致隐私问题。

某些浏览器能够接受有关用户点击链接时是否应发送 HTTP Referer 标头的提示;此提示通过 Referrer-Policy 标头 提供。此标头可以向浏览器建议三种行为中的任何一种

  • 完整 URL:在 Referer 标头中发送整个 URL。例如,如果用户正在访问 https://example.com/page.html,则 Referer 标头将包含 "https://example.com/page.html"

  • 仅限来源:仅在推荐人中发送“来源”。来源包括方案、主机和(可选)端口号。例如,如果用户正在访问 https://example.com/page.html,则来源将为 https://example.com/

  • 无推荐人:根本不发送 Referer 标头。

此标头可以告诉浏览器注意两种类型的条件

  • 同源与跨源:从 https://example.com/1.htmlhttps://example.com/2.html 的链接是同源链接。从 https://example.com/page.htmlhttps://not.example.com/page.html 的链接是跨源链接。

  • 协议降级:如果包含链接的页面通过 HTTPS 提供服务,但链接到的页面未通过 HTTPS 提供服务,则会发生降级。

警告

当您的网站通过 HTTPS 提供服务时,Django 的 CSRF 保护系统 需要 Referer 标头存在,因此完全禁用 Referer 标头将干扰 CSRF 保护。为了在保留 CSRF 保护的同时获得禁用 Referer 标头的大部分好处,请考虑仅启用同源推荐人。

SecurityMiddleware 可以根据 SECURE_REFERRER_POLICY 设置为您设置 Referrer-Policy 标头(注意拼写:当用户点击链接时,浏览器会发送 Referer 标头,但指示浏览器是否执行此操作的标头拼写为 Referrer-Policy)。此设置的有效值为

no-referrer

指示浏览器不为在此站点上点击的链接发送推荐人。

no-referrer-when-downgrade

指示浏览器发送完整的 URL 作为推荐人,但仅在不发生协议降级时发送。

origin

指示浏览器仅发送来源,而不是完整的 URL,作为推荐人。

origin-when-cross-origin

指示浏览器为同源链接发送完整的 URL 作为推荐人,并仅为跨源链接发送来源。

same-origin

指示浏览器发送完整的 URL,但仅限于同源链接。不会为跨源链接发送推荐人。

strict-origin

指示浏览器仅发送来源,而不是完整的 URL,并在发生协议降级时不发送推荐人。

strict-origin-when-cross-origin

指示浏览器在链接为同源且不发生协议降级时发送完整的 URL;在链接为跨源且不发生协议降级时仅发送来源;并在发生协议降级时不发送推荐人。

unsafe-url

指示浏览器始终发送完整的 URL 作为推荐人。

未知策略值

如果用户代理 不知道 策略值,则可以指定多个策略值以提供回退。理解的最后一个指定的策略值优先。为了支持这一点,可以使用可迭代对象或逗号分隔的字符串与 SECURE_REFERRER_POLICY 一起使用。

跨源打开程序策略

某些浏览器能够通过根据 跨源打开程序策略 (COOP) 标头的值将顶级窗口隔离到单独的浏览上下文组中来隔离顶级窗口与其他文档。如果以这种方式隔离的文档打开了一个跨源弹出窗口,则弹出窗口的 window.opener 属性将为 null。使用 COOP 隔离窗口是对跨源攻击的纵深防御,尤其是像 Spectre 这样的攻击,这些攻击允许泄露加载到共享浏览上下文中的数据。

SecurityMiddleware 可以根据 SECURE_CROSS_ORIGIN_OPENER_POLICY 设置为您设置 Cross-Origin-Opener-Policy 标头。此设置的有效值为

same-origin

将浏览上下文专门隔离到同源文档。跨源文档不会加载到相同的浏览上下文中。这是默认的也是最安全的选项。

same-origin-allow-popups

将浏览上下文隔离到同源文档或那些未设置 COOP 或通过设置 COOP 为unsafe-none来选择退出隔离的文档。

unsafe-none

允许将文档添加到其打开者的浏览上下文组,除非打开者本身的 COOP 为same-originsame-origin-allow-popups

X-Content-Type-Options: nosniff

某些浏览器会尝试猜测其获取的资产的内容类型,从而覆盖Content-Type标头。虽然这可以帮助显示配置不正确的服务器的站点,但也可能带来安全风险。

如果您的站点提供用户上传的文件,则恶意用户可能会上传经过特殊设计的文件,当您期望它为无害文件时,浏览器会将其解释为 HTML 或 JavaScript。

为了防止浏览器猜测内容类型并强制其始终使用Content-Type标头中提供的类型,您可以传递X-Content-Type-Options: nosniff标头。SecurityMiddleware如果SECURE_CONTENT_TYPE_NOSNIFF设置为True,则会对所有响应执行此操作。

请注意,在 Django 不参与提供用户上传文件的多数部署情况下,此设置对您没有帮助。例如,如果您的MEDIA_URL由您的前端 Web 服务器(nginx、Apache 等)直接提供服务,则您需要在那里设置此标头。另一方面,如果您使用 Django 执行诸如要求授权以下载文件之类的操作,并且无法使用您的 Web 服务器设置标头,则此设置将很有用。

SSL 重定向

如果您的站点同时提供 HTTP 和 HTTPS 连接,则大多数用户最终将默认使用不安全的连接。为了获得最佳安全性,您应该将所有 HTTP 连接重定向到 HTTPS。

如果您将SECURE_SSL_REDIRECT设置为 True,则SecurityMiddleware将永久(HTTP 301)将所有 HTTP 连接重定向到 HTTPS。

注意

出于性能考虑,最好在 Django 之外,在前端负载均衡器或反向代理服务器(如nginx)中执行这些重定向。SECURE_SSL_REDIRECT旨在用于无法使用此选项的部署情况。

如果SECURE_SSL_HOST设置有值,则所有重定向都将发送到该主机,而不是最初请求的主机。

如果您的站点上有一些页面应该可以通过 HTTP 访问,而不是重定向到 HTTPS,则可以在SECURE_REDIRECT_EXEMPT设置中列出与这些 URL 匹配的正则表达式。

注意

如果您部署在负载均衡器或反向代理服务器后面,并且 Django 似乎无法判断请求是否已经安全,则可能需要设置SECURE_PROXY_SSL_HEADER设置。

会话中间件

class SessionMiddleware[source]

启用会话支持。请参阅会话文档

站点中间件

class CurrentSiteMiddleware[source]

将表示当前站点的site属性添加到每个传入的HttpRequest对象。请参阅站点文档

身份验证中间件

class AuthenticationMiddleware[source]

将表示当前登录用户的user属性添加到每个传入的HttpRequest对象。请参阅Web 请求中的身份验证

class LoginRequiredMiddleware[source]

子类化中间件并覆盖以下属性和方法以自定义未经身份验证的请求的行为。

redirect_field_name

默认为"next"

get_login_url()[source]

返回未经身份验证的请求将重定向到的 URL。此结果要么是login_required()装饰器上设置的login_url(如果非None),要么是settings.LOGIN_URL

get_redirect_field_name()[source]

返回包含用户在成功登录后应重定向到的 URL 的查询参数的名称。此结果要么是login_required()装饰器上设置的redirect_field_name(如果非None),要么是redirect_field_name。如果返回None,则不会添加查询参数。

Django 5.1 中的新功能。

将所有未经身份验证的请求重定向到登录页面,除了使用login_not_required()排除的视图。登录页面默认为settings.LOGIN_URL,但可以自定义。

通过将其添加到MIDDLEWARE设置中AuthenticationMiddleware之后来启用此中间件。

MIDDLEWARE = [
    "...",
    "django.contrib.auth.middleware.AuthenticationMiddleware",
    "django.contrib.auth.middleware.LoginRequiredMiddleware",
    "...",
]

使用login_not_required()使视图公开,允许未经身份验证的请求。例如

from django.contrib.auth.decorators import login_not_required


@login_not_required
def contact_us(request): ...

使用login_required()装饰器自定义已验证视图的登录 URL 或字段名称,以分别设置login_urlredirect_field_name。例如

from django.contrib.auth.decorators import login_required
from django.utils.decorators import method_decorator
from django.views.generic import View


@login_required(login_url="/books/login/", redirect_field_name="redirect_to")
def book_dashboard(request): ...


@method_decorator(
    login_required(login_url="/books/login/", redirect_field_name="redirect_to"),
    name="dispatch",
)
class BookMetrics(View):
    pass
class RemoteUserMiddleware[source]

用于利用 Web 服务器提供的身份验证的中间件。有关用法详细信息,请参阅如何使用 REMOTE_USER 进行身份验证

class PersistentRemoteUserMiddleware[source]

仅在登录页面启用时用于利用 Web 服务器提供的身份验证的中间件。有关用法详细信息,请参阅仅在登录页面上使用 REMOTE_USER

CSRF 保护中间件

class CsrfViewMiddleware[source]

通过向 POST 表单添加隐藏表单字段并检查请求是否具有正确的值来防止跨站点请求伪造。请参阅跨站点请求伪造保护文档

X-Frame-Options中间件

class XFrameOptionsMiddleware[source]

通过 X-Frame-Options 头部提供简单的 点击劫持防护

中间件顺序

以下是一些关于各种 Django 中间件类顺序的提示

  1. SecurityMiddleware

    如果要开启 SSL 重定向,则应将其放在列表的顶部附近,这样可以避免运行一堆不必要的其他中间件。

  2. UpdateCacheMiddleware

    在修改 Vary 头部的中间件之前(SessionMiddlewareGZipMiddlewareLocaleMiddleware)。

  3. GZipMiddleware

    在任何可能更改或使用响应体(response body)的中间件之前。

    UpdateCacheMiddleware 之后:修改 Vary 头部。

  4. SessionMiddleware

    在任何可能引发异常以触发错误视图的中间件之前(例如,如果使用 CSRF_USE_SESSIONS,则为 PermissionDenied)。

    UpdateCacheMiddleware 之后:修改 Vary 头部。

  5. ConditionalGetMiddleware

    在任何可能更改响应的中间件之前(它设置 ETag 头部)。

    GZipMiddleware 之后,这样它就不会在压缩内容上计算 ETag 头部。

  6. LocaleMiddleware

    位于最顶部之一,在 SessionMiddleware(使用会话数据)和 UpdateCacheMiddleware(修改 Vary 头部)之后。

  7. CommonMiddleware

    在任何可能更改响应的中间件之前(它设置 Content-Length 头部)。出现在 CommonMiddleware 之前并更改响应的中间件必须重置 Content-Length

    接近顶部:当 APPEND_SLASHPREPEND_WWW 设置为 True 时,它会进行重定向。

    如果使用 CSRF_USE_SESSIONS,则在 SessionMiddleware 之后。

  8. CsrfViewMiddleware

    在任何假定已处理 CSRF 攻击的视图中间件之前。

    RemoteUserMiddleware 或任何其他可能执行登录并在调用中间件链之前旋转 CSRF 令牌的身份验证中间件之前。

    如果使用 CSRF_USE_SESSIONS,则在 SessionMiddleware 之后。

  9. AuthenticationMiddleware

    SessionMiddleware 之后:使用会话存储。

  10. LoginRequiredMiddleware

    Django 5.1 中的新功能。

    AuthenticationMiddleware 之后:使用用户对象。

  11. MessageMiddleware

    SessionMiddleware 之后:可以使用基于会话的存储。

  12. FetchFromCacheMiddleware

    在任何修改 Vary 头部的中间件之后:该头部用于为缓存哈希键选择一个值。

  13. FlatpageFallbackMiddleware

    应该接近底部,因为它是一种最后的中间件。

  14. RedirectFallbackMiddleware

    应该接近底部,因为它是一种最后的中间件。

返回顶部