SpringBoot之RestTemplate在URL中轉(zhuǎn)義字符的問題
RestTemplate在URL中轉(zhuǎn)義字符的問題
相同代碼在不同SpringBoot版本中使用RestTemplate請求時遇到的問題:
Map<String, Object> param = new HashMap<String, Object>();
param.put("version","2.0.0");
String userSing = sign(param);
param.put("user_sign", userSing);
logger.info("簽名:" + userSing);
StringBuilder paramStr = new StringBuilder("?");
for(Map.Entry<String, Object> entry : param.entrySet()){
paramStr.append(entry.getKey()).append("=")
.append(entry.getValue() == null ? "" : String.valueOf(entry.getValue()))
.append("&");
}
paramStr.deleteCharAt(paramStr.length() - 1);
logger.info("入?yún)?" + paramStr.toString());
RestTemplate restTemplate = new RestTemplate();
String jsonStr = restTemplate.getForObject(sendMessagesUrl + paramStr.toString(), String.class);
logger.info("響應值:" + jsonStr);下面截圖為springboot1.5.3 RestTemplate request log

下面截圖為springboot2.1.7 RestTemplate request log

在這兩份log中可以看到user_sign對應的value值中的 + 是特殊的字符,1.0版本的被轉(zhuǎn)義為: %2B,2.0版本沒有被轉(zhuǎn)義,最終2.0版本程序的RestTemplate請求第三方在簽名解碼時校驗不通過。
1.嘗試與分析
根據(jù)上述信息我們可以圈定問題的范圍,= 有被轉(zhuǎn)碼,說明可能是在RestTemplate中url參數(shù)的構(gòu)建轉(zhuǎn)碼的方式上與httpClient有什么不通,嘗試進行各類處理方法。
主要使用的方式有:
- UriComponent構(gòu)建uri(未解決)
- 構(gòu)建如下RestTemplateConfig(未解決)
public class RestTemplateConfig {
@Bean
RestTemplate restTemplate() {
SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
requestFactory.setReadTimeout(40000);
requestFactory.setConnectTimeout(40000);
// 添加轉(zhuǎn)換器
List<HttpMessageConverter<?>> messageConverters = new ArrayList<>();
messageConverters.add(new StringHttpMessageConverter(Charset.forName("UTF-8")));
messageConverters.add(new FormHttpMessageConverter());
messageConverters.add(new MappingJackson2HttpMessageConverter());
RestTemplate restTemplate = new RestTemplate(messageConverters);
restTemplate.setRequestFactory(requestFactory);
restTemplate.setErrorHandler(new DefaultResponseErrorHandler());
return restTemplate;
}
}2.跟蹤RestTemplate源碼

debug到這里時發(fā)現(xiàn)user_sign被轉(zhuǎn)義了但 + 沒有變。

這時URLDecoder.decode("/QaNSBls/U8ciXEWaCWtmeMK6+w%3D")會發(fā)現(xiàn) + 變成了 空格,導致第三方在簽名解碼時校驗不通過。


再次通過比較會發(fā)現(xiàn)StringBoot2.0版本對應的URL轉(zhuǎn)碼 + 不會被解析。
經(jīng)過多次百度有看到如下:
根據(jù) RFC 3986 加號等符號的確實可以出現(xiàn)在參數(shù)中的,而且不需要編碼,有問題的在于服務端的解析沒有與時俱進
3.解決問題
最后解決一些這個問題,當使用RestTemplate發(fā)起請求,如果請求參數(shù)中有需要url編碼時
通過如下兩種方式解決:
1.不希望出現(xiàn)問題的使用姿勢應傳入URI對象而不是字符串,修改RestTemplate請求方法入?yún)⒌?code>String url 修改為 URI url。
2.修改入?yún)⒕幋a格式URLEncoder.encode(user_sign, "UTF-8"),然后在構(gòu)建RestTemplate時,
Map<String, Object> param = new HashMap<String, Object>();
StringBuilder paramStr = new StringBuilder("?");
param.put("version","xxxxx");
param.put("time","xxxxx");
//轉(zhuǎn)化成md5生產(chǎn)密鑰
String userSing = sign(param);
String userSign = URLEncoder.encode(userSing, "UTF-8");
param.put("user_sign", userSign);
for(Map.Entry<String, Object> entry : param.entrySet()){
paramStr.append(entry.getKey()).append("=")
.append(entry.getValue() == null ? "" : String.valueOf(entry.getValue()))
.append("&");
}
paramStr.deleteCharAt(paramStr.length() - 1);
CloseableHttpClient httpClient = HttpClientUtils.acceptsUntrustedCertsHttpClient();
HttpComponentsClientHttpRequestFactory clientHttpRequestFactory = new HttpComponentsClientHttpRequestFactory(httpClient);
RestTemplate restTemplate = new RestTemplate(clientHttpRequestFactory);
DefaultUriBuilderFactory uriFactory = new DefaultUriBuilderFactory();
uriFactory.setEncodingMode(DefaultUriBuilderFactory.EncodingMode.VALUES_ONLY);
restTemplate.setUriTemplateHandler(uriFactory);
String smsJsonStr = restTemplate.getForObject(SMSURL + paramStr.toString(), String.class);
Map<String, Object> map = GsonUtil.gsonToMap(smsJsonStr);小結(jié)
注意SpringBoot2.0版本的url參數(shù)編碼,默認只會針對 = 和 & 進行處理;為了兼容我們一般的后端的url編解碼處理在需要編碼參數(shù)時,
個人建議盡量不要使用Spring默認的方式,不然接收到數(shù)據(jù)會和預期的不一致。
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關文章
mybatis中實現(xiàn)讓返回值與bean中字段相匹配
這篇文章主要介紹了mybatis中實現(xiàn)讓返回值與bean中字段相匹配,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-10-10
Spring Boot實現(xiàn)數(shù)據(jù)訪問計數(shù)器方案詳解
在Spring Boot項目中,有時需要數(shù)據(jù)訪問計數(shù)器,怎么實現(xiàn)數(shù)據(jù)訪問計數(shù)器呢?下面小編給大家?guī)砹薙pring Boot數(shù)據(jù)訪問計數(shù)器的實現(xiàn)方案,需要的朋友參考下吧2021-08-08
淺談Java并發(fā)中ReentrantLock鎖應該怎么用
本文主要介紹了ava并發(fā)中ReentrantLock鎖的具體使用,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-11-11
JavaWeb請求轉(zhuǎn)發(fā)和請求包含實現(xiàn)過程解析
這篇文章主要介紹了JavaWeb請求轉(zhuǎn)發(fā)和請求包含實現(xiàn)過程解析,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2020-02-02
Mybatis-Plus使用p6spy對SQL性能進行監(jiān)控的方法
這篇文章主要介紹了Mybatis-Plus使用p6spy對SQL性能進行監(jiān)控的方法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-12-12
淺談web服務器項目中request請求和response的相關響應處理
這篇文章主要介紹了淺談web服務器項目中request請求和response的相關響應處理,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-07-07
Spring Security+Spring Data Jpa如何進行安全管理
這篇文章主要介紹了Spring Security+Spring Data Jpa如何進行安全管理,幫助大家更好的理解和學習Spring Security框架,感興趣的朋友可以了解下2020-09-09

