auth_request模块仅转发请求并依据OAuth2服务返回的状态码判断鉴权结果;需配置proxy_pass指向introspect端点、透传Authorization/Host头、关闭请求体传输,并通过auth_request_set提取响应头传递用户信息,错误页需用error_page配合命名location跳转。

auth_request 模块本身不处理 OAuth2,它只做“转发+判断”:把请求交给外部 OAuth2 服务(如 Keycloak、Auth0 或自研 OAuth2 接口),然后看返回状态码。能不能用、好不好用,取决于你如何配置子请求路径、头信息透传和错误响应处理。
auth_request 怎么对接 OAuth2 服务?关键在 /auth location 的 proxy_pass 和头透传
OAuth2 服务通常需要原始请求的 Authorization 头(含 Bearer token)、Host、Referer 或 Origin 才能校验 token 有效性或 scope 权限。Nginx 默认不会原样透传所有头,必须显式设置:
-
proxy_pass必须指向 OAuth2 服务的真实地址(如<a href="https://www.php.cn/link/46e8db5f3c47012f108a81f53d640c82">https://www.php.cn/link/46e8db5f3c47012f108a81f53d640c82</a>) -
proxy_set_header Authorization $http_authorization显式透传认证头($http_authorization是 Nginx 内置变量) -
proxy_set_header Host $host防止 OAuth2 服务因 Host 不匹配拒绝请求 -
proxy_pass_request_body off大部分 OAuth2 introspect 接口只读 header,关掉体传输可避免超时或解析错误
常见错误现象:
- 返回 401 但没带
WWW-Authenticate头 → 前端无法跳转登录页(需在 OAuth2 服务响应中补上) - OAuth2 服务返回 200 但 body 是 JSON(如
{"active": true, "scope": "read"})→ Nginx 不关心 body,只看状态码,没问题 -
proxy_pass指向 HTTP 而 OAuth2 服务强制 HTTPS → 子请求失败,Nginx 默认视为拒绝(返回 500)
为什么不能直接用 auth_request /oauth2/authorize?
/oauth2/authorize 是重定向型端点(302),用于用户交互登录,不是鉴权接口。而 auth_request 要求子请求必须是同步、无重定向、快速返回状态码的接口。
OAuth2 中真正适合 auth_request 的是:
- Token Introspection 端点(RFC 7662),如
POST /oauth2/introspect - 自建的 JWT 校验接口(接收 token + URI,返回 200/401/403)
- Keycloak 的
/realms/{realm}/protocol/openid-connect/token/introspect
如果你强行配成 auth_request /oauth2/authorize:
- 子请求收到 302 → Nginx 视为非 2xx/3xx 响应 → 拒绝原始请求(返回 403)
- 即使加
proxy_redirect off,也无法绕过重定向逻辑,因为 OAuth2 authorize 不是设计给机器调用的
如何把 OAuth2 返回的 user_id、scope 传给后端服务?
auth_request_set 只能提取子请求响应头里的字段,不能解析 JSON body。所以 OAuth2 服务必须把关键信息放在响应头中,例如:
X-User-ID: abc123X-User-Scope: read writeX-Auth-Realm: production
然后在 Nginx 配置里:
auth_request_set $user_id $upstream_http_x_user_id; auth_request_set $scopes $upstream_http_x_user_scope; proxy_set_header X-User-ID $user_id; proxy_set_header X-User-Scopes $scopes;
注意:
-
$upstream_http_x_user_id中的下划线会被自动转为连字符(X-User-ID→$upstream_http_x_user_id),大小写不敏感但建议全小写+下划线 - 如果 OAuth2 服务返回的是
X-User-Id(驼峰),变量名得写成$upstream_http_x_user_id(Nginx 统一转为小写+下划线) - 若 OAuth2 服务没法改响应头,只能返回 JSON,那就得换方案:用 Lua(
nginx-lua-module)或改用 OpenResty 做解析,auth_request本身做不到
error_page 401 和 403 怎么导向登录页或统一错误页?
auth_request 触发 401/403 时,Nginx 默认直接返回对应状态码。要跳转,必须配合 error_page 指令:
location /api/ {
auth_request /auth;
error_page 401 = @login_redirect;
error_page 403 = @forbidden_page;
proxy_pass http://backend;
}
<p>location @login_redirect {
add_header Set-Cookie "redirect_uri=$scheme://$http_host$request_uri; Path=/; HttpOnly";
return 302 <a href="https://www.php.cn/link/2752de00a9d5eb87595fa04cd56f52e2">https://www.php.cn/link/2752de00a9d5eb87595fa04cd56f52e2</a>;
}</p><p>location @forbidden_page {
return 403 '{"error":"insufficient_scope"}';
}容易踩的坑:
-
error_page 401 = @login_redirect中的等号不能少,否则变成“内部重定向到命名 location”,而不是“用该 location 处理 401” -
@login_redirect里用return 302,不要用proxy_pass,否则又触发一次auth_request,死循环 - 如果 OAuth2 服务返回 401 但没带
WWW-Authenticate头,浏览器不会自动弹登录框,必须靠 302 跳转来驱动
真正的难点不在配置语法,而在于 OAuth2 服务是否提供轻量、无状态、低延迟的 introspect 接口——它一旦慢或挂,整个 API 流量就卡住。别把它当成“加个中间件就完事”的事。


















