Nginx与Symfony应用的Basic认证和Bearer认证冲突问题及可行方案咨询
我的问题
我目前正在开发一个Symfony Web应用,配置了测试和生产两套Nginx服务器。我的API计划使用Bearer令牌进行授权。
生产服务器部署在https://api.mydomain.de,测试环境则在https://dev.api.mydomain.de。
目前我在测试环境配置了Basic认证,规则如下:
location / { try_files $uri /index.php$is_args$args; auth_basic "Restricted Access"; auth_basic_user_file /etc/nginx/passwd/dev_passwd.txt; }
访问测试服务器时,需要在请求头中携带Authorization: Basic <token>。
但在测试用户系统时我发现,当应用本身也通过请求头做认证时,这套方案就会出现冲突:要么我用应用的有效登录凭证请求服务器,但Nginx不通过;要么反过来,Nginx认证通过了,但应用的认证不通过。
我已尝试的方案
我有几个解决思路,但不确定如何实现,甚至不知道该选哪一个:
1. 用auth_request校验Bearer令牌
我考虑过使用auth_request指令,通过向/user/validate-token/{token}这类端点发起子请求来校验Bearer令牌。但遇到了问题:auth_request不允许放在if语句块中,而且还出现了无法解决的502错误。
我目前写的配置如下:
location / { try_files $uri /index.php$is_args$args; # 检查Authorization头是否包含Bearer令牌 if ($http_authorization ~* ^Bearer\s+(.*)) { set $bearer_token $1; # 用内部位置校验令牌 auth_request /internal/token-validation; } auth_basic "Restricted Access"; auth_basic_user_file /etc/nginx/passwd/dev_passwd.txt; } location = /internal/token-validation { internal; proxy_pass http://localhost/user/validate-token/$bearer_token; proxy_pass_request_body off; proxy_set_header Content-Length ""; } # 这里是否需要移除basic认证? location /user/validate-token { try_files $uri /index.php$is_args$args; auth_basic "Restricted Access"; auth_basic_user_file /etc/nginx/passwd/dev_passwd.txt; } error_page 401 = @error_401; location @error_401 { return 401 "Unauthorized"; }
2. 使用Nginx内部位置/路由
我还想过创建单独的内部位置来分别处理Basic认证和Bearer令牌校验,这样能让逻辑更清晰分离,但也遇到了类似问题。而且我的应用会把所有请求URL识别为/basic_auth或/validate_bearer_token,因为请求被内部重写了。
我写的配置片段如下:
location /basic_auth { auth_basic "Restricted Access"; auth_basic_user_file /etc/nginx/passwd/dev_passwd.txt; rewrite ^/basic_auth(/.*)$ $1 break; try_files $uri $uri/ /index.php?$query_string; } location ~ ^/validate_bearer_token/(.*) { internal; # 仅内部可访问,外部无法直接调用 set $auth_token $1; # 将请求转发到校验端点 proxy_pass http://127.0.0.1/user/validate-token/$auth_token; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; # 拦截错误以处理令牌校验响应 proxy_intercept_errors on; error_page 401 = @error_401; # 不知道为什么我没法用200状态码 error_page 399 = @auth_success; } # [...]
3. 在应用中重命名Authorization头
我也考虑过在应用中重命名Authorization头,避免Basic认证和Bearer认证的冲突。但不确定这是不是最优解,而且我用的是Symfony + LexikJWTBundle,这个Bundle期望标准的Authorization头格式,我不知道修改会不会让集成变复杂,或者后续引发其他问题?甚至不确定这是否可行?
4. 不使用Basic认证
另一个思路是用自定义的密码头代替Nginx的Basic认证,由Symfony应用来校验这个头。不过我也能预见到问题,尤其是在https://dev.mydomain.de测试前端的时候,但我也愿意考虑这个方案。
我想知道,这些方案中哪个是客观上最优的?或者有没有我漏掉的其他方案?
备注:内容来源于stack exchange,提问作者Brentspine

