Nginx反向代理后Spring Boot服务401授权问题解决
看起来你遇到的问题是反向代理后Authorization头的处理或者后端服务的安全配置没跟上——毕竟直接访问后端正常,走代理就401,大概率是请求头传递或者后端对代理请求的信任配置出了问题。我来一步步帮你排查和解决:
第一步:先确认Authorization头是否真的被Nginx转发到后端
有时候配置写了,但实际请求头没传过去,这是最常见的问题。你可以在Spring Boot里加个简单的调试接口,打印所有请求头,验证代理后的请求是否携带了Authorization:
@RestController @RequestMapping("/debug") public class DebugController { @GetMapping("/headers") public Map<String, String> listAllHeaders(HttpServletRequest request) { Map<String, String> headerMap = new HashMap<>(); Enumeration<String> headerNames = request.getHeaderNames(); while (headerNames.hasMoreElements()) { String key = headerNames.nextElement(); headerMap.put(key, request.getHeader(key)); } return headerMap; } }
部署后,用curl分别测试直接访问和代理访问:
# 直接访问后端,确认能拿到Authorization头 curl -H "Authorization: Bearer YOUR_TOKEN" http://host/service/debug/headers # 走Nginx代理访问,检查返回的结果里有没有Authorization curl -H "Authorization: Bearer YOUR_TOKEN" http://mynginx/proxy/debug/headers
如果代理请求的返回里没有Authorization头,那问题出在Nginx配置上:
- 检查Nginx的
$http_authorization拼写是否正确(Nginx会把请求头转成小写,前缀加http_,所以Authorization对应$http_authorization是对的)。 - 确认有没有其他Nginx配置块(比如其他
location)拦截了这个请求,或者用proxy_hide_header Authorization隐藏了这个头。 - 可以尝试把
proxy_set_header Authorization $http_authorization;放到location /proxy/块的最前面,避免被其他proxy_set_header覆盖。
第二步:如果头已经传递,检查Spring Security对代理请求的信任配置
如果后端确实收到了Authorization头,但还是返回401,那大概率是Spring Security没识别出这是来自可信代理的请求,导致安全上下文初始化异常。你需要配置Spring Boot信任反向代理的X-Forwarded系列头:
方式1:通过配置文件快速搞定
在application.yml里添加:
server: forward-headers-strategy: framework tomcat: remoteip: remote-ip-header: X-Forwarded-For protocol-header: X-Forwarded-Proto
这个配置会让Spring Boot自动处理代理传递的X-Forwarded头,把请求的真实来源、协议等信息正确解析出来。
方式2:通过Spring Security配置类细化控制
如果需要更精细的配置,可以在Security配置类里添加:
@Configuration @EnableWebSecurity public class SecurityConfig { @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .authorizeHttpRequests(auth -> auth.anyRequest().authenticated()) .oauth2ResourceServer(oauth2 -> oauth2.jwt(Customizer.withDefaults())) // 信任来自代理的请求 .requestMatchers(req -> req.getHeader("X-Forwarded-Host") != null) // 无状态会话,适合REST服务 .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .csrf(csrf -> csrf.disable()); // REST服务通常不需要CSRF保护 return http.build(); } // 手动注册转发头过滤器(Spring Boot 2.2+如果用framework策略可以不用这个) @Bean public ForwardedHeaderFilter forwardedHeaderFilter() { return new ForwardedHeaderFilter(); } }
第三步:检查OAuth Token的Audience匹配
还有一种可能:你的OAuth Token里的aud(受众)字段只包含http://host/service,但走代理后请求的地址是http://mynginx/proxy,Spring Security验证Token时会检查受众是否匹配,不匹配就返回401。
解决方法是在Spring Security的OAuth2配置里添加允许的受众:
.oauth2ResourceServer(oauth2 -> oauth2 .jwt(jwt -> jwt .audience(audConfig -> audConfig .add("http://host/service") .add("http://mynginx/proxy") ) ) )
或者在你的OAuth授权服务器里,把代理后的地址也加入到允许的受众列表中。
最后:排查Nginx路径转发的细节
虽然你说直接访问后端正常,但也可以确认下Nginx的proxy_pass路径是否正确:你的location /proxy/转发到http://host/service/,这个配置会把/proxy/xxx转发到/service/xxx,路径重写是对的,但如果后端服务的接口路径依赖上下文路径,也可以检查下是否有冲突。
内容的提问来源于stack exchange,提问作者Khetho Mtembo

