JSON Web Token(JWT) - 实现系统集成授权访问
这是一个第三方系统访问AnyReport报表系统使用JWT授权的实现案例,AnyReport报表系统暴露报表资源URL供第三方系统访问,
第三方系统可以使用iframe,src设置为报表链接访问报表资源,这时资源URL类似restful api需要被认证的系统才能访问,
通过JWT的好处是不需要做登录认证在服务器端建立session。
JWT组成部分
JSON web Token 简称JWT,是一种token的URL安全方法,用于在网络通信的双方之间传递
JWT传输内容是由头部 header、负载 payload、签名 signature 三部分组成
header 定义Token 类型、加密类型,告诉服务器端使用的加密方法是HmacSHA256(HS256)
1
2
3
4
|
{
"typ"
:
"JWT"
,
"alg"
:
"HS256"
}
|
负载 payload 定义一些需要使用的信息:客户端ID、JWT创建的时间、用户ID,负载中不需要传输敏感信息如密码、密钥等,JWT内容通过HTTP传输不安全。
1
2
3
4
5
|
{
"cid"
:
"OA0001"
,
"iat"
: 1482656248798,
"uid"
:
"admin"
}
|
cid:是第三方系统的标示,因为可能多个第三方系统访问报表系统,报表系统可以根据cid查询密钥、失效时间等做JWT内容验证。
iat:创建JWT的时间,服务器端根据该iat判断JWT是否已经失效了,防止链接被别人获取。
uid:用户ID,服务器端可以根据该用户查询一些用户相关的信息,如一些权限信息,这个案例并没有使用这个字段。
签名 signature 部分是使用密钥对header Base64与payload Base64加密的Base64字符串。
JWT三个部分使用“.”链接的字符串, 最终JWT是:header base64字符串 + payload base64 字符串 + HmacSHA256(headerBase64 + "." + payloadBase64) base64字符串
1
|
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJjaWQiOiJPQTAwMDEiLCJpYXQiOjE0ODI2NTcyODQyMjF9.TeJpy936w610Vrrm+c3+RXouCA9k1AX0Bk8qURkYkdo=
|
JWT生成方法
JWT是由第三方系统生成的,可以使用服务器端语言,也可以使用Javascript生成,这种方式是最简单的,密钥保存在第三方系统服务器端当进入页面可以将密钥给js, js获取密钥而生成JWT,这里第三方系统密钥与报表系统密钥一致,各保留一份无需传输。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
function
token() {
var
base64 =
new
Base64();
//网上寻找一个base64库
var
header=
'{"typ":"JWT","alg":"HS256"}'
;
var
headerBase64 = base64.encode(header);
//header base64字符串
var
date =
new
Date();
var
payload =
'{"cid" : "OA0001","iat" : 1482656248798,"uid" : "admin"}'
;
var
payloadBase64 = base64.encode(payload);
//payload base64字符串
var
base64Token = headerBase64 +
"."
+ payloadBase64;
var
signature = CryptoJS.HmacSHA256(base64Token,
"123456"
);
//使用google的hmac-sha256.js库
var
signatureBase64 = base64.hex2b64(signature.toString());
var
jwt = base64Token +
"."
+ signatureBase64;
return
jwt;
}
|
生成的base64的token作为URL请求资源,url还需要做urlencoding编码
1
2
|
//url?jwt=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJjaWQiOiJPQTAwMDEiLCJpYXQiOjE0ODI2NTcyODQyMjF9.TeJpy936w610Vrrm%2Bc3%2BRXouCA9k1AX0Bk8qURkYkdo%3D
url?jwt=encodeURIComponent(token());
|
JWT验证
验证是在报表系统服务器端进行,验证部分比较简单的,采用Java解析JWT并做验证, 首选将jwt按“.“分割为parts三部分header,payload、signature, 使用js相同的密钥如“123456”进行加密,得到加密结果与jwt的signature进行比较,如果相等表示验证通过。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
public
static
boolean
verify(String jwt) {
String[] parts = jwt.split(
"\\."
);
String payload = StringUtils.newStringUtf8(Base64.decodeBase64(parts[
1
]));
JSONObject json = JSONObject.fromObject(payload);
String clientId = json.getString(
"cid"
);
long
iatTime = json.getLong(
"iat"
);
//验证jwt是否失效
if
(System.currentTimeMillis() - iatTime >
3600
*
1000
) {
return
false
;
}
//验证签名 signature
byte
[] content = (parts[
0
] +
"."
+ parts[
1
]).getBytes(StandardCharsets.UTF_8);
byte
[] signature = Base64.decodeBase64(parts[
2
]);
try
{
return
verifySignatureFor(
"HmacSHA256"
,
"123456"
, content, signature);
}
catch
(Exception e) {
throw
new
RuntimeException(e);
}
}
public
static
boolean
verifySignatureFor(String algorithm, String secret,
byte
[] contentBytes,
byte
[] signatureBytes)
throws
Exception {
byte
[] secretBytes = secret.getBytes(StandardCharsets.UTF_8);
Mac mac = Mac.getInstance(algorithm);
mac.init(
new
SecretKeySpec(secretBytes, algorithm));
return
MessageDigest.isEqual(mac.doFinal(contentBytes), signatureBytes);
}
|