Spring RestTemplate调用返回401未授权错误排查求助
问题排查:adminappPreProd调用appPreProd出现401未授权错误
背景信息
需要通过HTTP实现adminappPreProd服务调用appPreProd服务,两个服务的容器端口信息如下:
c053b74b7e3a app-pre_prod:latest "java -jar -Dspring.…" 0.0.0.0:8080->8080/tcp, :::8080->8080/tcp appPreProd
77d20ec0905c adminapp-pre_prod:latest "java -jar -Dspring.…" 0.0.0.0:8980->9080/tcp, :::9080->9080/tcp adminappPreProd
调用代码如下:
String tenantId = Long.toString(productUserMO.getId()); HttpHeaders headers = new HttpHeaders(); headers.set(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE); MultiValueMap<String, String> map = new LinkedMultiValueMap<String, String>(); map.add(Constants.X_TENANTID, tenantId); HttpEntity<?> entity = new HttpEntity<Object>(map, headers); restTemplate().exchange(apiURI.concat("/addPackageTypesForProductOwner"), HttpMethod.GET, entity, String.class);
调用时出现错误:
org.springframework.web.client.HttpClientErrorException$Unauthorized:
401 : "Unauthorized access"
已知情况:
- 服务未启用HTTPS
- 目标API已豁免授权校验,但请求并未到达appPreProd服务
- 调整请求头后问题仍未解决
排查及解决方案
1. 修复GET请求携带请求体的不规范写法
HTTP规范中GET请求不应该携带请求体,虽然部分客户端支持,但Spring MVC等服务端框架会直接拦截这类请求,导致请求无法到达业务代码,触发401错误。
两种修复方式:
- 方式一:将tenantId放到URL参数中
String tenantId = Long.toString(productUserMO.getId()); HttpHeaders headers = new HttpHeaders(); headers.set(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE); HttpEntity<?> entity = new HttpEntity<>(headers); // 拼接tenantId到URL参数 String targetUrl = apiURI.concat("/addPackageTypesForProductOwner") .concat("?").concat(Constants.X_TENANTID).concat("=").concat(tenantId); restTemplate().exchange(targetUrl, HttpMethod.GET, entity, String.class);
- 方式二:将tenantId放到请求头中
如果业务要求必须通过请求头传递tenantId,直接把它加入HttpHeaders即可,无需使用请求体:
String tenantId = Long.toString(productUserMO.getId()); HttpHeaders headers = new HttpHeaders(); headers.set(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE); headers.set(Constants.X_TENANTID, tenantId); // 直接添加到请求头 HttpEntity<?> entity = new HttpEntity<>(headers); restTemplate().exchange(apiURI.concat("/addPackageTypesForProductOwner"), HttpMethod.GET, entity, String.class);
2. 检查RestTemplate的拦截器配置
如果你的restTemplate()方法配置了全局拦截器,比如自动添加授权token的拦截器,可能会导致请求被中间件或服务端拦截。
- 排查
restTemplate()的初始化代码,看是否添加了ClientHttpRequestInterceptor类型的拦截器 - 如果存在不必要的授权拦截器,要么移除,要么针对该请求单独配置不带拦截器的RestTemplate实例
3. 确认API豁免授权的规则有效性
即使你配置了豁免,也要验证规则是否匹配:
- 检查豁免的URL路径是否和请求的路径完全一致(注意大小写、是否包含额外路径)
- 确认豁免规则是否包含GET请求方法,部分配置可能只豁免POST/PUT等方法
- 查看appPreProd服务的访问日志,如果日志中完全没有该请求的记录,说明请求在网关、服务网格等中间件层就被拦截了,需要检查中间件的认证规则
4. 验证服务间网络及中间件配置
- 先通过curl或postman从adminappPreProd容器内部测试访问appPreProd的8080端口,确认网络连通性
- 如果部署在容器集群中,检查是否有服务网格(比如Istio)或网关强制要求服务间认证,即使目标API豁免,中间件仍可能要求服务携带合法的身份凭证
内容的提问来源于stack exchange,提问作者pappu_kutty
相关产品推荐
相关产品推荐

