
Litestar 安全配置详解用 exclude、反向正则与 exclude_from_auth 精确控制端点鉴权范围【免费下载链接】litestarLight, flexible and extensible ASGI framework | Built to scale项目地址: https://gitcode.com/GitHub_Trending/li/litestar在 Litestar 中启用认证后哪些端点需要登录、哪些端点必须放行是安全配置里最常见也最容易出错的问题。本篇基于仓库文档 excluding-and-including-endpoints.rst 展开系统讲解安全后端SessionAuth、JWTAuth、JWTCookieAuth中exclude正则规则的匹配语义与锚点陷阱、如何用一条反向正则实现只保护指定路由以及如何通过路由处理器的exclude_from_auth或自定义exclude_opt_key选项对单个端点做精细化放行。读完后你可以独立完成任何组合的端点鉴权范围配置并从源码层面理解这些规则在 ASGI 中间件中的实际执行链路。适用前提先配置好安全后端本篇讨论的是各类安全后端的exclude规则因此需要先了解如何在应用上注册一个安全后端如SessionAuth、JWTAuth、JWTCookieAuth相关设置方式见 security-backends.rst。下文的示例均以SessionAuth为例同样的规则对 jwt/auth.py 中的JWTAuth和JWTCookieAuth完全一致。默认规则配置在Auth对象AbstractSecurityConfig的子类上。从 security/base.py 可以看到基类定义了控制鉴权范围的全部字段# litestar/security/base.py exclude: str | list[str] | None None A pattern or list of patterns to skip in the authentication middleware. exclude_opt_key: str exclude_from_auth An identifier to use on routes to disable authentication and authorization checks for a particular route. exclude_http_methods: Sequence[Method] | None field( default_factorylambda: cast(Sequence[Method], [OPTIONS, HEAD]) ) A sequence of http methods that do not require authentication. Defaults to [OPTIONS, HEAD] scopes: Scopes | None None ASGI scopes processed by the authentication middleware, if None, both http and websocket will be processed.也就是说除了路径级的excludeLitestar 还提供了两个隐性的放行维度exclude_http_methods默认[OPTIONS, HEAD]这些方法的请求不经过认证这也是 CORS 预检请求通常能直接通过的原因scopes默认同时处理http和websocket两种 ASGI scope。排除路由exclude 的正则语义与锚点陷阱exclude参数接收一个str或str列表被解释为正则模式并与完整路径做匹配。这里有一个必须牢记的语义细节这些模式不会被隐式锚定not implicitly anchored。因此像/schema这样的模式会匹配任何包含/schema的路径而不仅仅是以它开头的路径。只希望匹配以某前缀开头的路径时必须用^显式锚定。例如下面的配置表示除以下三类路径外的所有端点都要求认证——^/login以/login开头的路由^/signup以/signup开头的路由;^/schema以/schema开头的路由。由于^/schema已经覆盖了前缀匹配你不必再单独排除/schema/swagger——它天然被该模式覆盖。session_auth SessionAuthUser, ServerSideSessionBackend, # exclude any URLs that should not have authentication. # We exclude the documentation URLs, signup and login. exclude[r^/login, r^/signup, r^/schema], ) ...更完整的可运行示例含登录/用户获取逻辑见 using_session_auth.py。危险模式/会关闭全部路由的认证文档中特别用 danger 提示传入/会关闭所有路由的认证因为作为正则它匹配每一个路径。这一点在源码层面同样得到了印证。middleware/_utils.py 中的build_exclude_path_pattern负责把用户传入的模式编译成正则并且在编译成功后做一次贪心检查如果模式同时能匹配/和一个随机 UUID 路径就会发出warn_middleware_excluded_on_all_routes警告——# litestar/middleware/_utils.py pattern re.compile(|.join(exclude)) if not isinstance(exclude, str) else re.compile(exclude) if pattern.match(/) and pattern.match(/982c7064-6ac7-44b7-9be5-07a2ff6d8a92): # match a UUID to ensure that it matches paths greedily and not just a literal / warn_middleware_excluded_on_all_routes(pattern, middleware_clsmiddleware_cls) return pattern即即使你在生产环境没有刻意传/只要你的模式组合意外地覆盖了所有路径比如写成了(?!x).*这类能吞掉一切的正则Litestar 会在运行时主动告警而不是让一个全放行的认证配置悄无声息地上线。另外如果传入的正则本身非法该函数会直接抛出ImproperlyConfiguredExceptionUnable to compile exclude patterns for middleware...把配置错误暴露在启动阶段。为什么包含即匹配match_exclude_path 的源码证据非锚定、包含即命中的语义可以在 middleware/_utils.py 的match_exclude_path中得到确认——它用findall而非match或fullmatch对路径求值# litestar/middleware/_utils.py def match_exclude_path(exclude_path_pattern: Pattern, scope: Scope) - bool: return bool( exclude_path_pattern.findall( scope[raw_path].decode() if getattr(scope.get(route_handler, None), is_mount, False) else scope[path] ) )两个值得注意的细节findall意味着模式可以落在路径的任意位置这正是不加^就匹配任意包含位置的底层原因对于 mount子应用挂载路由匹配发生在scope[raw_path]上保留 URL 编码的原始路径普通路由则用解码后的scope[path]。如果你的排除模式涉及带编码字符的路径如 Unicode 段这一差异需要纳入考虑。反向规则只保护指定路由既然排除规则按正则求值就可以构造一条反转规则——除了指定模式命中的路径之外其余所有路径都被排除在认证之外。下面的例子中只有/secured端点要求认证其余路由全部放行... session_auth SessionAuthUser, ServerSideSessionBackend, # exclude any URLs that should not have authentication. # We exclude the documentation URLs, signup and login. exclude[r^(?!.*\/secured$).*$], ) ...这里的正则^(?!.*\/secured$).*$使用了负向前瞻negative lookahead^(?!...)要求从头开始不匹配.*\/secured$以/secured结尾随后.*$吞掉整条路径。效果上凡是路径以/secured结尾的请求都不被排除即需要认证其他任何路径都会被这条模式排除即放行。需要强调的是这条规则的语义与文档描述一致只有/secured之下的端点受认证保护其他路由不受保护——如果你的业务默认应该是全保护、少数放行优先使用上一节的正向排除列表把反向规则留给默认开放、少数收紧的场景。精确放行单个端点exclude_from_auth 与自定义 exclude_opt_key正则排除工作在中间件层、按路径匹配而当整条路由都需要认证、只是其中一两个端点例外时逐路径写正则会比较繁琐。更自然的做法是在路由处理器上打一个跳过认证的标记把exclude_from_authTrue传给该 handler... get(/secured) def secured_route() - Any: ... get(/unsecured, exclude_from_authTrue) def unsecured_route() - Any: ...默认选项键exclude_from_auth来自 security/base.py 中的exclude_opt_key字段默认值。同时你可以在安全配置中通过exclude_opt_key换一个更符合项目习惯的键名例如改用no_auth... get(/secured) def secured_route() - Any: ... get(/unsecured, no_authTrue) def unsecured_route() - Any: ... session_auth SessionAuthUser, ServerSideSessionBackend, # exclude any URLs that should not have authentication. # We exclude the documentation URLs, signup and login. exclude[r^/login, r^/signup, r^/schema], exclude_opt_keyno_auth # default value is exclude_from_auth ) ...从源码结构看这个选项的生效链路非常直接AbstractAuthenticationMiddleware.__call__在每次请求时调用should_bypass_middleware见 middleware/authentication.py后者依次检查四类放行条件——# litestar/middleware/_utils.py if scope[type] not in scopes: return True if ( exclude_opt_key and (route_handler : scope.get(route_handler)) is not None and route_handler.opt.get(exclude_opt_key) ): return True if exclude_http_methods and scope.get(method) in exclude_http_methods: return True return exclude_path_pattern is not None and match_exclude_path(exclude_path_pattern, scope)也就是说scope 类型检查请求类型不在scopes配置内则直接跳过选项键检查从scope[route_handler].opt中读取exclude_opt_key指定的键为真即放行——get(/unsecured, no_authTrue)里的no_auth就是这样被读到的HTTP 方法检查默认放行OPTIONS与HEAD路径模式检查最后才落到exclude正则。一旦判定 bypass中间件会完全跳过authenticate_request直接await self.app(scope, receive, send)否则才执行认证并把结果写入scope[user]与scope[auth]之后即可通过connection.user、connection.auth访问。完整执行链路从配置到每次请求把上述片段串起来一条带认证的请求在 Litestar 中的判定流程是安全后端如SessionAuth在应用初始化时通过on_app_initsecurity/base.py把自己的DefineMiddleware注入app_config.middleware同时把guards、dependencies、OpenAPI security 组件一并挂载中间件构造时middleware/authentication.pyexclude列表经build_exclude_path_pattern用|连接编译成单一re.Pattern非法正则在启动期抛错、过宽模式触发告警每个请求进入__call__时按should_bypass_middleware的四级检查决定是否认证需要认证时调用后端的authenticate_request各后端自行实现会话/JWT 校验结果写入 scope失败则抛出NotAuthorizedException/PermissionDeniedException。这套行为有专门的测试用例覆盖可参考 test_base_authentication_middleware.py其中test_authentication_middleware_exclude_from_auth验证了get(..., exclude_from_authTrue)与exclude[south, east]组合后各路径的鉴权结果test_authentication_middleware_exclude_from_auth_custom_key则验证了通过exclude_from_auth_keymy_exclude_key自定义选项键的等价行为与上文exclude_opt_key的用法一一对应。小结三种放行机制如何选择机制作用位置适用场景关键配置exclude正则列表安全配置中间件层按路径前缀/模式批量放行登录、文档、回调exclude[r^/login, r^/schema]注意^锚定反向正则安全配置中间件层默认开放、仅收紧个别路径exclude[r^(?!.*\/secured$).*$]exclude_from_auth/ 自定义exclude_opt_key路由处理器装饰器同一路由树下个别端点例外get(/unsecured, no_authTrue)exclude_opt_keyno_auth三点实践提醒前缀匹配必须写^否则/schema也会放行/admin/schema-internal这类意外路径避免全放行模式如/即使误写运行时警告warn_middleware_excluded_on_all_routes也会提示你复核除路径外OPTIONS/HEAD默认免认证、scopes默认覆盖 WebSocket配置时应对这两项有明确预期必要时显式设置exclude_http_methods与scopes。掌握以上规则后你可以把 using_session_auth.py 等示例与本文的排除规则组合起来覆盖 Litestar 安全后端在绝大多数场景下的端点鉴权范围需求。【免费下载链接】litestarLight, flexible and extensible ASGI framework | Built to scale项目地址: https://gitcode.com/GitHub_Trending/li/litestar创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考