文章目录
- 前言
- 一、"+"为什么被替换:
-
- [2.1 URL 编码(Percent-Encoding)的核心规则](#2.1 URL 编码(Percent-Encoding)的核心规则)
- [2.2 GET 请求中 + 被替换为空格的完整流程](#2.2 GET 请求中 + 被替换为空格的完整流程)
- [2.3 为什么 Base64 密文容易出现这个问题?](#2.3 为什么 Base64 密文容易出现这个问题?)
- 二、怎么处理:
-
- [2.1 使用 URL 安全编码](#2.1 使用 URL 安全编码)
- [2.2 生成的 URL 示例(编码前后对比)](#2.2 生成的 URL 示例(编码前后对比))
- [2.3 后端Get接收参数自动解码](#2.3 后端Get接收参数自动解码)
- [2.4 注意事项](#2.4 注意事项)
- 总结
前言
问题场景:后端生成并返回给前端的字符串 中 有 "+" 但是前端 通过Get 请求将改参数传入后端后 "+"被替换成了" "。
一、"+"为什么被替换:
GET 请求中遇到 + 被替换为空格的核心原因是:+ 是 URL 编码中的特殊字符(代表空格),当包含 + 的字符串直接作为 GET 参数传输时,浏览器 / HTTP 客户端会自动将 + 解析为空格,服务器接收时就会出现替换问题------ 这是 URL 编码规范(RFC 3986)和 HTTP 传输的固有特性,并非代码错误,而是参数传输格式不符合规范导致的。
2.1 URL 编码(Percent-Encoding)的核心规则
URL 只能包含「字母、数字、- _ . ~」等安全字符,其他字符(如 /、+、=、& 等)属于「保留字符」或「不安全字符」,必须通过 %+十六进制ASCII码 编码后传输:
空格的 URL 编码是 %20,但早期为了简化,也常用 + 表示空格(这是 application/x-www-form-urlencoded 格式的约定); + 本身的 ASCII 码是 43,对应的 URL 编码是 %2B
2.2 GET 请求中 + 被替换为空格的完整流程
bash
原始字符串:...ec+khJ1Qh...
↓ 浏览器/HTTP客户端(如OkHttp、HttpClient)自动解析
识别 `+` 为 URL 中的「空格占位符」
↓ 传输到服务器
服务器接收参数时,将 `+` 解码为空格 → ...ec khJ1Qh...
2.3 为什么 Base64 密文容易出现这个问题?
当传输的参数是 Base64 编码的密文,而 Base64 编码的字符集包含 +、/、= 三个特殊字符:
+:对应 URL 中的空格;
/:URL 中的路径分隔符;
=:URL 参数中的键值对分隔符;
这三个字符直接放在 GET 参数中都会被篡改,其中 + 是最容易触发的问题。
二、怎么处理:
2.1 使用 URL 安全编码
使用 JDK 自带 java.net.URLEncoder 类,可直接实现标准 URL 编码(注意字符集必须用 UTF-8),通过URLEncoder.encode(content, StandardCharsets.UTF_8); 进行编码
代码如下(示例):
c
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
/**
* URL 编码工具类(标准 URLEncode)
*/
public class UrlEncodeUtil {
/**
* 标准 URL 编码(适配 GET 参数)
* @param content 待编码内容(如 Base64 密文)
* @return 编码后的字符串
*/
public static String encode(String content) {
if (content == null || content.trim().isEmpty()) {
return "";
}
// 关键:字符集必须用 UTF-8,且编码空格为 %20(而非 +)
return URLEncoder.encode(content, StandardCharsets.UTF_8);
}
/**
* 标准 URL 解码(后端接收参数时 SpringBoot 自动解码,此方法仅备用)
* @param encodedContent 编码后的字符串
* @return 原始内容
*/
public static String decode(String encodedContent) {
if (encodedContent == null || encodedContent.trim().isEmpty()) {
return "";
}
try {
return java.net.URLDecoder.decode(encodedContent, StandardCharsets.UTF_8);
} catch (Exception e) {
throw new RuntimeException("URL 解码失败", e);
}
}
}
2.2 生成的 URL 示例(编码前后对比)
| 处理阶段 | 内容示例 |
|---|---|
| 原始令牌(Base64) | 954nFKV42Ox7K0qCdd/oE2wj3hJ4B/51I0IHSFWDHgztgBqxOE0ec+khJ1QhOEAP... |
| URL 编码后 | 954nFKV42Ox7K0qCdd%2FoE2wj3hJ4B%2F51I0IHSFWDHgztgBqxOE0ec%2BkhJ1QhOEAP... |
可以看到:
+ 被编码为 %2B;
/ 被编码为 %2F;
=(若有)被编码为 %3D;
这些编码后的字符在 GET 请求传输中不会被篡改,服务器接收后会自动还原。
2.3 后端Get接收参数自动解码
请求发送到后端时,SpringBoot 会自动对 @RequestParam 注解的参数做 URL 解码,无需手动处理:
java
@GetMapping("/reset-password")
public String showResetPage(@RequestParam String token) {
// token 已经是 URL 解码后的原始 Base64 令牌(+、/ 已还原)
System.out.println("接收的令牌(已解码):" + token);
// 后续验证令牌、展示重置页面...
return "reset-password";
}
2.4 注意事项
- 字符集必须统一:编码和解码都必须使用 UTF-8,否则会出现乱码(URLEncoder.encode 若不指定字符集,JDK8 及以上默认 UTF-8,但建议显式指定)。
- 避免重复编码:不要对已编码的 URL 再次编码(如 encode(encode(token))),否则会导致 %2B 被编码为 %252B,服务器解码后得到 %2B 而非 +。
- 特殊字符全覆盖:除了 +,Base64 中的 /、= 也会被 URL 解析器处理,标准 URLEncode 会一次性处理所有特殊字符,无需单独替换。
- URL 长度限制:GET 请求的 URL 有长度限制(不同浏览器 / 服务器约 2048 字符),若你的令牌过长(如 512 位),建议: 优先使用 POST 请求传输令牌;或缩短令牌长度(如优化加密内容)。
总结
在使用Get 请求传递参数是+, /、= 如果不进行 URLEncode 进行编码,服务器会将改字符进行篡改,导致后端接收的参数与实际值出现偏差。