【spring boot】RestTemplate 链接带签名post请求 400 bad request
2023-12-15 08:42:36
由于项目需要从服务端对第三方发起请求,而且第三方没有提供SDK的情况下,只能根据对方api文档发送请求了,对方接口的格式是:地址+签名,post请求上送具体参数的方式去请求对方服务。
背景
很简单的一个需求,然而开始就卡住了,在Apifox调用能正常返回数据,而一用restTemplate去请求,就报400错误。
org.springframework.web.client.HttpClientErrorException$BadRequest: 400 Bad Request
at org.springframework.web.client.HttpClientErrorException.create(HttpClientErrorException.java:79)
at org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:122)
at org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:102)
at org.springframework.web.client.ResponseErrorHandler.handleError(ResponseErrorHandler.java:63)
at org.springframework.web.client.RestTemplate.handleResponse(RestTemplate.java:778)
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:736)
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:670)
at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:579)
...
Apifox能调通,而代码调不通,不外乎代码创建起来的https请求有问题
分析
本来没打算细究,出错了打算改用OkHttpClient
试一下,还真调通了
private void action(String uri){
OkHttpClient client = new OkHttpClient();
RequestBody body = null;
body = RequestBody.create(MediaType.parse("application/json"), data);// data是jsonObject转的String数据
Request request = new Request.Builder().url(uri)
.method("POST", body)
.addHeader("Content-Type", "application/json")
.build();
try {
Response execute = client.newCall(request).execute();
System.out.println(execute);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
感觉有点离谱,所以直接用抓包工具(这里用的是fiddler,刚学着用,踩了点坑)抓包一下
POST https://xxx/xx/request?AccessKeyId=xxx&Expires=xx&Signature=xxx&Timestamp=2023-12-11T03%253A39%253A36Z HTTP/1.1
Accept: application/json, application/*+json
Content-Type: application/json;charset=UTF-8
User-Agent: Java/1.8.0_221
Host: xxxxx
Connection: keep-alive
Content-Length: xx
{"data":"0"}
返回数据是
HTTP/1.1 400 Bad Request
Date: Mon, 11 Dec 2023 03:39:38 GMT
Content-Type: text/plain; charset=utf-8
Transfer-Encoding: chunked
Connection: keep-alive
9d
{"code":"","message":"日期格式错误,请使用yyyy-MM-ddTHH:mm:ssZ格式"}
0
很明显的错误提示“日期格式错误,请使用yyyy-MM-ddTHH:mm:ssZ格式
”,回头看下上送的报文是“Timestamp=2023-12-11T03%253A39%253A36Z
”,虽然不怎么熟悉url的编码解码,但是很明显%253A
实际上是%3A
既“:
”,所以是因为时间被双重加密了,因为在拼接url的时候,我已经手动给时间进行了URL编码
URLEncoder.encode(String.valueOf(time), "UTF-8")
所以去掉后再来
POST https://xxx/xx/request?AccessKeyId=xxx&Expires=xx&Signature=***&Timestamp=2023-12-11T08:25:21Z HTTP/1.1
Accept: application/json, application/*+json
Content-Type: application/json;charset=UTF-8
User-Agent: Java/1.8.0_221
Host: xxxxx
Connection: keep-alive
Content-Length: xx
{"data":"0"}
返回数据
HTTP/1.1 401 Unauthorized
Date: Mon, 11 Dec 2023 08:25:21 GMT
Content-Type: text/plain; charset=utf-8
Transfer-Encoding: chunked
Connection: keep-alive
6c
{"error":{"code":"AuthFailure","message":"签名错误"}}
0
玩我呢…我不格式化它也不格式化,只能断点分析了。发现RestTemplate这里对url进行了处理
@Override
@Nullable
public <T> T execute(String url, HttpMethod method, @Nullable RequestCallback requestCallback,
@Nullable ResponseExtractor<T> responseExtractor, Object... uriVariables) throws RestClientException {
// 这里getUriTemplateHandler调用了一个默认的处理器对url进行了处理
URI expanded = getUriTemplateHandler().expand(url, uriVariables);
return doExecute(expanded, method, requestCallback, responseExtractor);
}
解决
所以,只要改变这个处理器就可以解决问题了
@Bean
public RestTemplate restTemplate(){
RestTemplate restTemplate = new RestTemplate();
DefaultUriBuilderFactory uriFactory = new DefaultUriBuilderFactory();
// 这里选择了不处理而是自己手动去处理编码了,所以就不再会出现之前的问题了
uriFactory.setEncodingMode(DefaultUriBuilderFactory.EncodingMode.NONE);
restTemplate.setUriTemplateHandler(uriFactory);
// ...
return restTemplate;
}
总结
很多时候,其实在编码的时候会下意识自信自己编写的没有问题, 以至于在调试的时候很难去发现问题点(开始的时候,其实我先断点了请求,发现请求是成功出去了,也没能关注到%253A的问题,一个劲的根据其他文章说的协议、请求头什么的问题在尝试),所以当发现问题但又自认为自己没有问题的时候,不妨换个角度换个方式,或者重新从头写一遍(有时候逻辑多的时候,其实也不要怕,一边写一边复盘会比自己只干看着分析还可能好一点)当然具体情况要结合自己实际去行动了。
文章来源:https://blog.csdn.net/Athain/article/details/134921536
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!