前言
在OpenID Connect协议中,如果登录的时候指定了response_type=id_token
的话,
在重定向回到第三方应用时,会将id_token的值作为queryString参数放在url中,这里
就涉及一个问题:
如何避免id_token和url的特殊字符冲突?
URL编码规范
关于URL编码的详细规范,可以查看RFC 3986, 这里只对我们会涉及的信息做简要说明。
RFC 3986中有如下两个重要规定:
- Url中只允许包含英文字母(a-zA-Z)、数字(0-9)、
-
_
.
~
4个特殊字符以及所有保留字符。 - 保留字符包括:
!
*
'
(
)
;
:
@
&
=
+
$
,
/
?
#
[
]
另外,还有很多字符,当它们直接放在Url中的时候,可能会引起解析程序的歧义。
这些字符被视为不安全字符,原因有很多。
- 空格:Url在传输的过程,或者用户在排版的过程,或者文本处理程序在处理Url的过程,都有可能引入无关紧要的空格,或者将那些有意义的空格给去掉;
- 引号以及<>:引号和尖括号通常用于在普通文本中起到分隔Url的作用;
- #:通常用于表示书签或者锚点;
- %:百分号本身用作对不安全字符进行编码时使用的特殊字符,因此本身需要编码;
{
}
|
\
^
[
]
`
~
:某一些网关或者传输代理会篡改这些字符。
url安全的base64编码
在实际的应用中,id_token是一个jwt字符串,字符串的载体是一个base64编码的json字符串,这就可能出现不符合url编码规范的结果,
因为base64编码会出现+
,/
,=
这些被url转码的特殊字符,一旦这个结果被转码了,第三方应用获取到的base64字符串和服务端
签名生成的就不一致了,因此对id_token的编码必须是url安全的base64编码。
url安全的base64编码规范一般采用如下方式:
- 将base64中的
+
号换成-
号 - 将base64中的
/
号换成_
号 - 将base64中的
=
号换成空字符串,即删除=
号
对应的,url安全的base64解码过程要反过来:
- 将base64中的
-
号换成+
号 - 将base64中的
_
号换成/
号 - 在base64中末位补齐
=
号,使得base64的字符数量可以被4整除
java编码示例代码如下:
public static String safeUrlBase64Encode(byte[] data){
String encodeBase64 = new BASE64Encoder().encode(data);
String safeBase64Str = encodeBase64.replace('+', '-');
safeBase64Str = safeBase64Str.replace('/', '_');
safeBase64Str = safeBase64Str.replaceAll("=", "");
return safeBase64Str;
}
java解码示例代码如下:
public static byte[] safeUrlBase64Decode(final String safeBase64Str){
String base64Str = safeBase64Str.replace('-', '+');
base64Str = base64Str.replace('_', '/');
int mod4 = base64Str.length()%4;
if(mod4 > 0){
base64Str = base64Str + "====".substring(mod4);
}
return new BASE64Decoder().decodeBuffer(base64Str);
}