现象
最近定位到一个有意思的bug,后端生成的URL中包含base64参数值后,经过tomcat重定向后,偶发出现前端无法解密的现象。
原因
当出现问题时,经排查发现重定向的Location响应头中把+转成了英文空格,导致解密失败。
重定向时如果特殊字符未经URLEncode转义,则tomcat会把+转换成英文空格。
处理方案
方案1、对Base64参数值进行UrlEncode。推荐
此方案会将所有特殊符号替换成%数字字母格式,如%2B,最后一个字母大小写不敏感。
Java代码:
/* by yours.tools - online tools website : yours.tools/zh/textdiff.html */ String plainText = "hello>"; String encodedText = Base64.getEncoder().encodeToString(plainText.getBytes(StandardCharsets.UTF_8)); String urlEncodedText = URLEncoder.encode(url, StandardCharsets.UTF_8.toString()); response.sendRedirect("http://127.0.0.1:8080/test?encode=" + urlEncodedText); //http://127.0.0.1:8080/test?encode=aGVsbG8%2BJavaScript代码:
/* by yours.tools - online tools website : yours.tools/zh/textdiff.html */ const encode = 'aGVsbG8%2B' const decode = atob(decodeURIComponent(encode)) console.log(decode) //hello>方案2、Base64使用UrlEncoder进行加密。需前端配合
基于2006年的RFC规范:RFC4648 URL安全字符串替换(+替换成-,/替换成_)
前端的JS方法atob()和btoa()首个版本即支持;ECMAScript 2024+的Uint8Array.fromBase64()也是支持的。
Java代码:
String plainText = "hello>"; String encodedText = Base64.getUrlEncoder().encodeToString(plainText.getBytes(StandardCharsets.UTF_8)); response.sendRedirect("http://127.0.0.1:8080/test?encode=" + encodedText); //http://127.0.0.1:8080/test?encode=aGVsbG8-JavaScript代码:
//写法1:替换回-为+,_为/,补全=,使用atob()完成base64解密 const encodeText = 'aGVsbG8-' let encode = encodeText.replace(/-/g, '+') .replace(/_/g, '/') .padEnd(encodeText.length + (4 - encodeText.length % 4) % 4, '='); const result = atob(decodeURIComponent(encode)) console.log(result) //写法2:替换后使用TextDecoder解密 <script src="https://unpkg.com/base64-js/base64js.min.js"></script> <script> function decodeBase64Url(base64UrlString) { // 转换为标准Base64 const base64 = base64UrlString .replace(/-/g, '+') .replace(/_/g, '/') .padEnd(base64UrlString.length + (4 - base64UrlString.length % 4) % 4, '='); // 解码 const byteArray = base64js.toByteArray(base64); return new TextDecoder().decode(byteArray); } console.log(decodeBase64Url('aGVsbG8-')) </script>方案3、调整tomcat配置。
不推荐,可自行搜索。