JWT简介
token进行用户身份验证流程: ①客户端使用用户名+密码请求登录 ②服务端收到请求验证用户名和密码 ③验证成功后,服务端发送一个token给客户端 ④客户端将token保存起来 ⑤后续请求资源需要携带这个token ⑥服务端进行token验证,验证成功则执行请求
token相比于session优点: ①节约服务器资源,对移动端友好 ②支持跨域访问(跨域名访问):cookie无法跨域,而token放到请求头中可不使用cookie,故跨域后不存在信息丢失问题(即发送token不是针对某个域名单独进行,任何域名使用的token可以相同,而cookie对不同域名不同处理) ③无状态:用token后,服务端不需要存储session信息,因为token包含了所有登录用户的信息,可减轻服务端压力 ④更适合CDN ⑤更适用于移动端 ⑥无需考虑CSRF(cookie中跨站请求伪造,即盗取cookie信息进行冒名登陆)
JWT:JSON Web Token,token的一种具体实现方式 本身就是一个字符串,将用户信息保存到JSON字符串并编码后得到(有签名信息)
JWT结构
由三部分组成:①标头(Header)②有效载荷(Payload)③签名(Signature) 传输时会将三部分分别进行Base64编码后,用‘.’进行拼接形成字符串
Header JWT头是一个描述JWT元数据的JSON对象,alg表示签名用的算法,typ表示令牌的属性(统一JWT)
{
"alg": "HS256",
"typ": "JWT"
}
Payload 有效载荷是JWT主体内容部分,也是JSON对象,包含需要传递的数据,有如下七个字段: ①iss:发行人 ②exp:到期时间 ③sub:主题 ④aud:用户 ⑤nbf:在此之前不可用 ⑥iat:发布时间 ⑦jti:JWT ID,用于标识该JWT 上述是JWT预定义可以选用的字段,还可以额外自定义私有字段。 给字段赋值拼接为JSON后就作为JWT的Payload部分。 注:JWT默认情况下是未加密的,只用Base64算法,可以通过内容获取传递的信息,故类似密码等用户敏感信息不能通过JWT传递。
Signature 签名哈希部分是对上述两部分数据的签名,需要使用base64编码后的header和payload数据,通过指定算法生成哈希,确保数据不被篡改。 首先需要指定一个秘钥,该密码保存在服务器中,使用签名算法(默认HMAC SHA256)生成签名。
作用 header和payload可以直接用base64获得原文。 header用于获取哈希签名使用的算法,payload获取具体数据 signature作为上述的整合,作用是检验token是否被篡改,利用获得的算法和秘钥对前两部分加密,比对加密后数据和客户端发送的是否一致。
JWT java使用
首先引入依赖:
compile 'com.auth0:java-jwt:3.4.0'
JWT生成token:
public void testGenerateToken(){
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.SECOND, 10);
String token = JWT.create()
.withHeader(new HashMap<>())
.withClaim("userId", 21)
.withClaim("userName", "baobao")
.withExpiresAt(calendar.getTime())
.sign(Algorithm.HMAC256("!34ADAS"));
System.out.println(token);
}
头部是hashmap键值对,payload部分可以手动设置,签名部分需要由用户传入秘钥。
解析JWT字符串:
public static String checkUserToken(String token) {
if (token != null && token.length() > 1) {
try {
DecodedJWT decodeJwt = JWT.require(Algorithm.HMAC256("123")).build().verify(token);
if (decodeJwt != null) {
Date expriteat = decodeJwt.getExpiresAt();
if (expriteat.getTime() < new Date().getTime()) {
return "null";
}
return decodeJwt.getClaim("id").asString();
}
} catch (Exception e) {
e.printStackTrace();
return "";
}
}
return null;
}
解析token字符串后,可以通过其自身预定义的字段decodeJwt.getExpiresAt() 获取数据,也可以通过decodeJwt.getClaim("id") 获取自定义字段。
JWT安卓使用
但上述操作在安卓可以生成JWT字符串,但解析时由于Base64冲突,无法进行。 故在安卓解析部分需要使用另一个库:
implementation 'com.auth0.android:jwtdecode:1.1.1'
客户端只需要用base64解析header和payload部分获取数据即可
public boolean checkUserToken(String token) {
if (token != null && token.length() > 1) {
JWT jwt = new JWT(token);
Date expiresAt = jwt.getExpiresAt();
Claim abc = jwt.getClaim("abc");
System.out.println(abc.asInt());
if (expiresAt.getTime() < System.currentTimeMillis()) {
return false;
}
return true;
}
return false;
}
解析token字符串后,可以通过其自身预定义的字段jwt.getExpiresAt() 获取数据,也可以通过jwt.getClaim("abc") 获取自定义字段。
注:JAVA使用方法会将JWT检测篡改,故传入的参数还需要秘钥、算法进行签名的解码,结合其解析,说明该解法通常用于服务器,客户端不应该知道密码,并且也不需要认识篡改。 安卓用法就比较纯粹,单纯取出header和payload部分进行base64解码获取数据。
|