【异常】使用RestTemplate调用Http请求,赋值host属性无效问题
问题背景
https://stackoverflow.com/questions/9096987/how-to-overwrite-http-header-host-in-a-httpurlconnection/9098440
在与第三方系统联调时,给出的文档需要在 headers中给host属性添加对方指定的值。联调过程中发现调用失败。
使用抓包命令发现headers中host内容并不是程序设置的内容。
抓包语句
tcpdump -i any host 1.1.1.1 and port 8445 -A -vvv --过滤ip为1.1.1.1端口为8445的消息,可以看到headers内容
赋值失效原因
- 使用RestTemplate调用http请求时,Jdk代码会在设置headers时进行一些受限的校验,判断是否进行限制校验
- 受限制的key包括:
"Access-Control-Request-Headers", "Access-Control-Request-Method", "Connection", "Content-Length", "Content-Transfer-Encoding", "Host", "Keep-Alive", "Origin", "Trailer", "Transfer-Encoding", "Upgrade", "Via" - 标记是否进行校验的key的名:
sun.net.http.allowRestrictedHeaders,默认false
- 受限制的key包括:
- 如果是要进行校验,并且key在上述范围之内,在构建headers时不会进行赋值,使用系统默认值。
解决方案
在启动类 添加代码
System.setProperty("sun.net.http.allowRestrictedHeaders", "true");
启动类中添加是因为http校验会在初始化的时候给allowRestrictedHeaders属性赋值
代码分析
使用RestTemplate 进行http调用,jdk在sun.net.www.protocol.http.HttpURLConnection#isRestrictedHeader方法中进行参数校验
调用链

主要涉及
isRestrictedHeader:是否限制的header信息isExternalMessageHeaderAllowed:是否允许添加的header信息addRequestProperty:添加请求参数
源码
//调用代码
public class MovieController {
@Resource
private RestTemplate restTemplate;
@GetMapping("/user/{id}")
public User findById(@PathVariable("id") Long id){
// restTemplate.headForHeaders("host","123123");
return restTemplate.getForObject("http://localhost:8001/"+id, User.class);
}
}
//jdk代码
public class HttpURLConnection extends java.net.HttpURLConnection {
.....
static {
....
//类初始化时,赋值 allowRestrictedHeaders
allowRestrictedHeaders = (Boolean)AccessController.doPrivileged(new GetBooleanAction("sun.net.http.allowRestrictedHeaders"));
....
}
//添加的headers属性
public synchronized void addRequestProperty(String var1, String var2) {
if (!this.connected && !this.connecting) {
if (var1 == null) {
throw new NullPointerException("key is null");
} else {
if (this.isExternalMessageHeaderAllowed(var1, var2)) { //校验当前值是否允许添加
this.requests.add(var1, var2);//给headers添加程序的赋值
if (!var1.equalsIgnoreCase("Content-Type")) {
this.userHeaders.add(var1, var2);
}
}
}
} else {
throw new IllegalStateException("Already connected");
}
}
//是否允许添加的header信息
private boolean isExternalMessageHeaderAllowed(String var1, String var2) {
this.checkMessageHeader(var1, var2);
return !this.isRestrictedHeader(var1, var2);
}
//是否限制的header信息
private boolean isRestrictedHeader(String var1, String var2) {
if (allowRestrictedHeaders) {//是否需要进行校验 默认false ,可以通过系统参数进行配置
return false;
} else {
var1 = var1.toLowerCase();
//restrictedHeaderSet中的值 【Access-Control-Request-Headers", "Access-Control-Request-Method", "Connection", "Content-Length", "Content-Transfer-Encoding", "Host", "Keep-Alive", "Origin", "Trailer", "Transfer-Encoding", "Upgrade", "Via】,如果在这个范围,就不进行赋值
if (restrictedHeaderSet.contains(var1)) {
return !var1.equals("connection") || !var2.equalsIgnoreCase("close");
} else {
return var1.startsWith("sec-");
}
}
}
.....
}

浙公网安备 33010602011771号