发现bug之后,我开始思考对密文进行编码,一开始使用了URLEncoder.encode("****","utf-8")
这种方式来进行编码,但是编码之后发现编码之后的密文和编码之前的密文完全一致。和同事聊了一下这个现象,猜测是javascript
编码方式不一致。于是我开始探索web
端使用的编码方式——escape
escape
函数
JavaScript escape() 函数
该方法不会对 ASCII 字母和数字进行编码,也不会对下面这些 ASCII 标点符号进行编码: * @ - _ + . / 。其他所有的字符都会被转义序列替换。
可以使用 unescape() 对 escape() 编码的字符串进行解码。
当我试图从网上寻找Java
版本的escape
函数时,看见一种思路:
- 将数字、大小写的字母不进行处理
- 对每个非汉字的特殊符号(
ASCII
值小于256)前增加%
,然后将该符号转ASCII
值,并且以十六进制展示。 - 对每个中文前增加
%u
,然后对该文字转ASCII
值,并且以十六进制展示。如将“中”转为%u4E2D
这种思路和上面文档的定义严重违背,不符合上面文
档的说明,测试失败。
继续翻,发现网上有一个java
版本的Escape
工具类出现频率很高。根据懒人原则第一条,复制代码到项目,测试是否可用。准备关闭项目摸鱼的时候发现一个问题.先卖个关子,看看这个方法和上面方法的区别:
- 将数字、大小写的字母以及
['-','_','.','!','~','*','/','(',')']
数组内的特殊字符不处理 - 将空格转为
+
- 对每个非汉字的特殊符号(
ASCII
值小于128)前增加%
,然后将该符号转ASCII
值,并且以十六进制展示。如¥
转为%A5
- 对每个中文前增加
%u
,然后对该文字转ASCII
值,并且以十六进制展示。如将“中”转为%u4E2D
扫了一眼,发现这个方法和定义不对,定义里面只有7种特殊符号,也没有对空格和+
号做阐述,于是测试了一波,发现果然还是坑:
escape(" “) // 运行结果:”%20"
于是再次修改,这次只根据JavaScript escape() 函数定义来编辑,得到如下代码:
private fun escape(src: String): String {
var i = 0
var j: Char
val tmp = StringBuffer()
tmp.ensureCapacity(src.length * 6)
while (i < src.length) {
j = src[i]
when {
Character.isDigit(j)
|| Character.isLowerCase(j)
|| Character.isUpperCase(j)
|| specialSymbols.contains(j) -> tmp.append(j)
j.toInt() < 128 -> {
tmp.append("%")
if (j.toInt() < 16) tmp.append(“0”)
tmp.append(j.toInt().toString(16))
}
else -> {
tmp.append("%u")
tmp.append(j.toInt().toString(16))
}
}
i++
}
return tmp.toString()
}
既然有编码,那肯定得有对应的解码方法,这个也很简单:
fun unescape(src: String): String {
val tmp = StringBuffer()
tmp.ensureCapacity(src.length)
var lastPos = 0
var pos = 0
var ch: Char
while (lastPos < src.length) {
pos = src.indexOf("%", lastPos)
if (pos == lastPos) {
when {
src[pos + 1] == ‘u’ -> {
ch = src
.substring(pos + 2, pos + 6).toInt(16).toChar()
tmp.append(ch)
lastPos = pos + 6
}
else -> {
ch = src
.substring(pos + 1, pos + 3).toInt(16).toChar()
tmp.append(ch)
lastPos = pos + 3
}
}
} else {
lastPos = if (pos == -1) {
tmp.append(src.substring(lastPos))
src.length
} else {
tmp.append(src.substring(lastPos, pos))
pos
}
}
}
return tmp.toString()
}
三、反思
照理来说,这个时候应该可以摸鱼了,但是有一个疑问深深的困惑着我,为什么解码/编码没有统一的方法?难道真的存在java
和javascript
编码不一致吗?于是我打开了URLEncoder.encode
源码:
/**
- Translates a string into {@code application/x-www-form-urlencoded}
- format using a specific encoding scheme. This method uses the
- supplied encoding scheme to obtain the bytes for unsafe
- characters.
-
- Note: The <a href=
- “http://www.w3.org/TR/html40/appendix/notes.html#non-ascii-chars”>
- World Wide Web Consortium Recommendation states that
- UTF-8 should be used. Not doing so may introduce
- incompatibilities.
- @param s {@code String} to be translated.
- @param enc The name of a supported
- character
- encoding.
- @return the translated {@code String}.
- @exception UnsupportedEncodingException
-
If the named encoding is not supported
- @see URLDecoder#decode(java.lang.String, java.lang.String)
- @since 1.4
*/
public static String encode(String s, String enc)
throws UnsupportedEncodingException {
boolean needToChange = false;
StringBuffer out = new StringBuffer(s.length());
Charset charset;
CharArrayWriter charArrayWriter = new CharArrayWriter();
if (enc == null)
throw new NullPointerException(“charsetName”);
try {
charset = Charset.forName(enc);
} catch (IllegalCharsetNameException e) {
throw new UnsupportedEncodingException(enc);
} catch (UnsupportedCharsetException e) {
throw new UnsupportedEncodingException(enc);
}
for (int i = 0; i < s.length()😉 {
int c = (int) s.charAt(i);
//System.out.println("Examining character: " + c);
if (dontNeedEncoding.get©) {
if (c == ’ ') {
c = ‘+’;
needToChange = true;
t c = (int) s.charAt(i);
//System.out.println("Examining character: " + c);
if (dontNeedEncoding.get©) {
if (c == ’ ') {
c = ‘+’;
needToChange = true;