JWT简介与使用(安卓注意事项)
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(){
// 指定token过期时间为10秒
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.SECOND, 10);
String token = JWT.create()
.withHeader(new HashMap<>()) // Header
.withClaim("userId", 21) // Payload
.withClaim("userName", "baobao")
.withExpiresAt(calendar.getTime()) // 过期时间
.sign(Algorithm.HMAC256("!34ADAS")); // 签名用的secret
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解码获取数据。