SpringBoot之RestTemplate在URL中转义字符的问题

 更新时间:2023年06月26日 08:57:33   作者:MLn0829  
这篇文章主要介绍了SpringBoot之RestTemplate在URL中转义字符的问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

RestTemplate在URL中转义字符的问题

相同代码在不同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("入参:" + paramStr.toString());
            RestTemplate restTemplate = new RestTemplate();
            String jsonStr = restTemplate.getForObject(sendMessagesUrl + paramStr.toString(), String.class);
            logger.info("响应值:" + jsonStr);

下面截图为springboot1.5.3 RestTemplate request log

springboot1.5.3 RestTemplate request log

下面截图为springboot2.1.7 RestTemplate request log

springboot2.1.7 RestTemplate request log

在这两份log中可以看到user_sign对应的value值中的 + 是特殊的字符,1.0版本的被转义为: %2B2.0版本没有被转义,最终2.0版本程序的RestTemplate请求第三方在签名解码时校验不通过。

1.尝试与分析

根据上述信息我们可以圈定问题的范围,= 有被转码,说明可能是在RestTemplate中url参数的构建转码的方式上与httpClient有什么不通,尝试进行各类处理方法。

主要使用的方式有:

  • UriComponent构建uri(未解决)
  • 构建如下RestTemplateConfig(未解决)
public class RestTemplateConfig {
    @Bean
    RestTemplate restTemplate() {
        SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
        requestFactory.setReadTimeout(40000);
        requestFactory.setConnectTimeout(40000);
        // 添加转换器
        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源码

RestTemplate源码

debug到这里时发现user_sign被转义了但 + 没有变。

URLDecode

这时URLDecoder.decode("/QaNSBls/U8ciXEWaCWtmeMK6+w%3D")会发现 + 变成了 空格,导致第三方在签名解码时校验不通过。

isAllowed

再次通过比较会发现StringBoot2.0版本对应的URL转码 + 不会被解析。

经过多次百度有看到如下:

根据 RFC 3986 加号等符号的确实可以出现在参数中的,而且不需要编码,有问题的在于服务端的解析没有与时俱进

3.解决问题

最后解决一些这个问题,当使用RestTemplate发起请求,如果请求参数中有需要url编码时

通过如下两种方式解决:

1.不希望出现问题的使用姿势应传入URI对象而不是字符串,修改RestTemplate请求方法入参的String url 修改为 URI url

2.修改入参编码格式URLEncoder.encode(user_sign, "UTF-8"),然后在构建RestTemplate时,

Map<String, Object> param = new HashMap<String, Object>();
        StringBuilder paramStr = new StringBuilder("?");
            param.put("version","xxxxx");
            param.put("time","xxxxx");
            //转化成md5生产密钥
            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);

小结

注意SpringBoot2.0版本的url参数编码,默认只会针对 = 和 & 进行处理;为了兼容我们一般的后端的url编解码处理在需要编码参数时,

个人建议尽量不要使用Spring默认的方式,不然接收到数据会和预期的不一致。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • RocketMQ重试机制及消息幂代码实例解析

    RocketMQ重试机制及消息幂代码实例解析

    这篇文章主要介绍了RocketMQ重试机制及消息幂代码实例解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-02-02
  • Spring零基础入门IOC

    Spring零基础入门IOC

    IoC就是比方说有一个类,我们想要调用类里面的方法(不是静态方法),就要创建该类的对象,使用对象调用方法来实现。但对于Spring来说,Spring创建对象的过程,不是在代码里面实现的,而是交给Spring来进行配置实现的
    2022-08-08
  • 简单聊一聊Spring中Bean别名的处理原理

    简单聊一聊Spring中Bean别名的处理原理

    今天来和小伙伴们聊一聊 Spring 中关于 Bean 别名的处理逻辑,别名,顾名思义就是给一个 Bean 去两个甚至多个名字,整体上来说,在 Spring 中,有两种不同的别名定义方式,感兴趣的小伙伴跟着小编一起来看看吧
    2023-09-09
  • mybatis高级映射一对多查询实现代码

    mybatis高级映射一对多查询实现代码

    本篇文章主要介绍了mybatis高级映射一对多查询实现代码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下。
    2017-04-04
  • java基于QuartzJobBean实现定时功能的示例代码

    java基于QuartzJobBean实现定时功能的示例代码

    QuartzJobBean是Quartz框架中的一个抽象类,用于定义和实现可由Quartz调度的作业,本文主要介绍了java基于QuartzJobBean实现定时功能的示例代码,具有一定的参考价值,感兴趣可以了解一下
    2023-09-09
  • Springboot mybatisplus如何解决分页组件IPage失效问题

    Springboot mybatisplus如何解决分页组件IPage失效问题

    这篇文章主要介绍了Springboot mybatisplus如何解决分页组件IPage失效问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-08-08
  • 关于Java中@SuppressWarnings的正确使用方法

    关于Java中@SuppressWarnings的正确使用方法

    这篇文章主要介绍了关于Java中@SuppressWarnings的正确使用方法,@SuppressWarnings注解主要用在取消一些编译器产生的警告对代码左侧行列的遮挡,有时候这会挡住我们断点调试时打的断点,需要的朋友可以参考下
    2023-05-05
  • MyBatis-Plus找不到Mapper.xml文件的几种解决方法

    MyBatis-Plus找不到Mapper.xml文件的几种解决方法

    mybatis-plus今天遇到一个问题,就是mybatis 没有读取到mapper.xml 文件,所以下面这篇文章主要给大家介绍了关于MyBatis-Plus找不到Mapper.xml文件的几种解决方法,需要的朋友可以参考下
    2022-06-06
  • SpringBoot集成RocketMQ发送事务消息的原理解析

    SpringBoot集成RocketMQ发送事务消息的原理解析

    RocketMQ 的事务消息提供类似 X/Open XA 的分布事务功能,通过事务消息能达到分布式事务的最终一致,这篇文章主要介绍了SpringBoot集成RocketMQ发送事务消息,需要的朋友可以参考下
    2022-06-06
  • Java使用Apache POI库读取Excel表格文档的示例

    Java使用Apache POI库读取Excel表格文档的示例

    POI库是Apache提供的用于在Windows下读写各类微软Office文档的Java库,这里我们就来看一下Java使用Apache POI库读取Excel表格文档的示例:
    2016-06-06

最新评论