踩坑URL的参数中有加号传值变为空格的问题

2023年 9月 6日 86.1k 0

踩坑经历

最近官网上线了一个限时优惠的活动,活动参与者可以通过分享链接获得优惠,然后在群里和社区就有小伙伴反映说分享之后用户点击分享链接并没有增加分享次数,什么?居然有 bug!

企业微信截图_16934568118910.png

image.png

活动是这样的,当用户点击生成分享链接的时候,我们会将该用户的一些信息进行加密为一个字符串,拼在他要分享的项目地址后面,然后被分享的人点击这个分享链接我们再对加密字符串进行解密,看看是谁的分享链接,然后给这个人增加分享次数,然而问题就出现在当别人点了分享链接并没有给分享者增加次数,于是我和我的小伙伴立刻开始寻找问题,我们首先查看了线上服务器的日志,发现报解密失败异常,这就离谱了,难道加解密有问题?
正当我疑惑时,我的小伙伴和我说他发现服务器解密的字符串和加密之后的字符串对不上。

企业微信截图_1693894830666.png

也就是说客户端向服务器传递参数时,参数中的“+”全部变成了空格,原因是URL中默认的将“+”号转义了。

解决思路

上面我们已经发现了坑在哪里,接下来我会介绍三种填坑方式给大家,大家记得点赞收藏哦。

使用 URLEncoder

通常URL地址中有"中文字符串"传递时,才会使用到这两个类,这样就可以将传递过来的中文接受后,再还原成原来的中文字符串.如不转换,则通过URL传递过来的中文字符中会变成乱码,无法还原了。那我们一起来看看我们的加密字符串经过 URLEncoder 编码之后会变成什么样,可以解码吗?带着疑问我们来做个测试

public class CryptoTest {

  /**
   * 测试 URLEncoder
   */
  @Test
  public void testURLEncoder() throws UnsupportedEncodingException {
    String encryptData = "9tdbrQJPhrd9BGVhx2bG5l789Vrsj1QzTaUNEHwzZKawi3L/EHO/P9Gg11FqMg/1E6C9a9ZHhofX1yBDxyY3ZY4hG9478K21+UXSHr5d2drz3utkvfWjVuNBWHgAfIhx";
    String encode = URLEncoder.encode(encryptData, "UTF-8");
    System.out.println(encode);
    // 9tdbrQJPhrd9BGVhx2bG5l789Vrsj1QzTaUNEHwzZKawi3L%2FEHO%2FP9Gg11FqMg%2F1E6C9a9ZHhofX1yBDxyY3ZY4hG9478K21%2BUXSHr5d2drz3utkvfWjVuNBWHgAfIhx

    String decode = URLDecoder.decode(encode);
    System.out.println(decode);
    // 9tdbrQJPhrd9BGVhx2bG5l789Vrsj1QzTaUNEHwzZKawi3L/EHO/P9Gg11FqMg/1E6C9a9ZHhofX1yBDxyY3ZY4hG9478K21+UXSHr5d2drz3utkvfWjVuNBWHgAfIhx
  }
}

不错,使用 jkd 自带的 URLEncoder 可以将我们字符串中的 “+” 转为 “%2B”,而且也能正常解码,所以使用 URLEncoder 是可以解决我们的这个问题的。

使用 UriUtils

虽然上面的 URLEncoder 可以解决我们的问题,但是,如果在我们的字符串中包含空格,那么经过 URLEncoder 编码之后空格会变为 “+”,什么?防不胜防啊,所以当我们的字符串中包含空格时使用 URLEncoder 还是会出现问题,也就是说jdk自带的URL编码工具类 URLEncoder 在对字符串进行URI编码的时候,会把空格编码为 “+” 号。 这时就需要使用 spring 提供的 UriUtils 来代替 URLEncoder 进行编码了,UriUtils 会将空格编码为 “%20”,这个坑这次帮你铲平了,希望以后不会再遇到了。

public class CryptoTest {

  @Test
  public void testURLEncoder() throws UnsupportedEncodingException {
    String encryptData = "x y";
    String encode = URLEncoder.encode(encryptData, "UTF-8");
    System.out.println(encode);
    //x+y
  }
} 

使用 spring 提供的 UriUtils 来代替 URLEncoder 进行编码

public class CryptoTest {

  @Test
  public void testUriUtils() throws UnsupportedEncodingException {
    String encryptData = "x y";
    String encode = UriUtils.encode(encryptData, "UTF-8");
    System.out.println(encode);
    //x%20y
  }
} 

使用 Base64

Base64 能够将任何数据转换为易移植的字符串,避免了传输过程中失真问题。
需要注意的是,Base 64不是一种加密方式,只是一种编码方式。很多时候,我们都将Base64编码作为数据加密后的传输 / 存储格式

public class Base64Util {

  private static Base64.Encoder encoder = Base64.getEncoder();
  private static Base64.Decoder decoder = Base64.getDecoder();

  /**
   * Base64编码,byte[] 转 String
   * @param bytes byte[]
   * @return 字符串
   */
  public static String encodeBytesToString(byte[] bytes){
    return encoder.encodeToString(bytes);
  }

  /**
   * Base64解码,String 转 byte[]
   * @param text 字符串
   * @return byte[]
   */
  public static byte[] decodeStringToBytes(String text){
    return decoder.decode(text);
  }

  /**
   * Base64编码,String 转 String
   * @param text 字符串
   * @return Base64格式字符串
   */
  public static String encode(String text){
    return encoder.encodeToString(text.getBytes(StandardCharsets.UTF_8));
  }

  /**
   * Base64解码,String 转 String
   * @param base64Text Base64格式字符串
   * @return 字符串
   */
  public static String decode(String base64Text){
    return new String(decoder.decode(base64Text), StandardCharsets.UTF_8);
  }
}

使用 Base64 对我们的加密字符串进行编码测试

public class CryptoTest {

  /**
   * 测试 Base64
   */
  @Test
  public void testBase64(){
    // 测试 Base64 加解密
    String encode = Base64Util.encode("9tdbrQJPhrd9BGVhx2bG5l789Vrsj1QzTaUNEHwzZKawi3L/EHO/P9Gg11FqMg/1E6C9a9ZHhofX1yBDxyY3 ZY4hG9478K21+UXSHr5d2drz3utkvfWjVuNBWHgAfIhx");
    System.out.println(encode);
    //OXRkYnJRSlBocmQ5QkdWaHgyYkc1bDc4OVZyc2oxUXpUYVVORUh3elpLYXdpM0wvRUhPL1A5R2cxMUZxTWcvMUU2QzlhOVpIaG9mWDF5QkR4eVkzIFpZNGhHOTQ3OEsyMStVWFNIcjVkMmRyejN1dGt2ZldqVnVOQldIZ0FmSWh4
    System.out.println(Base64Util.decode(encode));
    //9tdbrQJPhrd9BGVhx2bG5l789Vrsj1QzTaUNEHwzZKawi3L/EHO/P9Gg11FqMg/1E6C9a9ZHhofX1yBDxyY3 ZY4hG9478K21+UXSHr5d2drz3utkvfWjVuNBWHgAfIhx
  }
}

从测试结果来说,使用 Base64Util 对我们的加密字符串进行编码也是没有问题的,经过浏览器之后也能正常解析。

总结

但是使用 Base64Util 编码之后,可以明显看到编码后的字符串长度比 URLEncoder 或 UriUtils 长了一些,如果你对这个字符串长度有要求,希望尽可能短一些,那你可以使用 URLEncoder 或 UriUtils,具体使用哪个就看你字符串有没有空格,有空格就使用 UriUtils,反之就使用 URLEncoder,好了,这篇主要给大家分享了我的踩坑经历与解决思路,希望对大家有帮助,最后如果想要了解加解密的工具可以看下一篇,拜了个拜。

链接

  • 博客地址

相关文章

JavaScript2024新功能:Object.groupBy、正则表达式v标志
PHP trim 函数对多字节字符的使用和限制
新函数 json_validate() 、randomizer 类扩展…20 个PHP 8.3 新特性全面解析
使用HTMX为WordPress增效:如何在不使用复杂框架的情况下增强平台功能
为React 19做准备:WordPress 6.6用户指南
如何删除WordPress中的所有评论

发布评论