JWT的定义
JWT(Json Web Token)是一个轻巧的开放标准(RFC 7519),它定义了一种安全可靠的传输信息的方式,所传输的信息是一个‘小巧’和‘自包含’的JSON对象。作为一个标准,它没有具体的技术实现,不过现在大多数语言平台都按照其标准提供了具体的技术实现。我们在实际的使用过程中,只需要根据具体的技术平台,使用相应的实现库就可以。
使用场景
在实际使用中:JWT主要解决以下两个问题:
认证: 由于JWT本身‘小巧’和‘自包含’的特点,最常见的用法是通过它来完成基于token的身份认证,服务端在用户首次账号和密码登录成功之后会返回JWT,后续的请求都需要带有这个JWT来作为身份认证的标识。。
信息安全传输:JWT的组成部分包含有数字签名,这让它可以作为一种安全传输信息的方式。
在平台服务化越来越普遍的今天,JWT可以作为开放API的权限认证一种方法,在合法客户端访问开放API时需要先从服务端获取JWT,在随后的请求中则需要在请求中携带JWT,服务端在验证JWT的有效性后再提供相应的服务。
JWT的结构
JWT是一个字符串,由三部分组成,分别是头部(header),载荷(payload)和签名(signature)。
格式如下:
header.payload.signature
头部(header):
JWT的头部用于描述JWT最基本的信息,如类型和签名所用的算法, 可表示为下面的json结构:
{
"typ":"JWT",
"alg":"HS256"
}
在生成JWT的header过程中会对这个json字符串进行BASE64编码为
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
载荷(Payload)
Payload里面是Token的具体内容,里面是一些标准字段,你也可以添加一些自定义字段,标准字段如下
iss:Issuer,发行者
sub:Subject,主题
aud:Audience,观众
exp:Expiration time,过期时间
nbf:Not before
iat:Issued at,发行时间
jti:JWT ID
比如下面这个Payload,包含有标准字段iss发行人和exp过期时间,还有自定义字段name
{
"iss":"org.zy",
"exp":"148895645"
"name": "zy",
}
在生成JWT的Payload过程中也会对这个json字符串进行BASE64编码为
eyJpc3MiOiJvcmcuenkiLCJuYW1lIjoienkiLCJleHAiOiIxNDg4OTU2NDUifQ
这个时候你可能有疑惑,通过BASE64编码等同于传输的都是明文,如何保证安全?下面我们一起看看第三部分签名(Signature)
签名(Singature)
这部分是把BASE64编码的Header和Payload按照Header.Payload格式进行加密,加密算法在Header中已经指明,加密的密钥secretKey通常存在服务端。
最终生成的JWT为:
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJvcmcuenkiLCJuYW1lIjoienkiLCJleHAiOiIxNDg4OTU2NDUifQ.MrP_jnb8ujP6WgcrP5G79IC4ojvVsX-UvwCSaiubbL8
至此,JWT字符串已经生成。这个JWT字符串作为后续请求服务的token。客户端把此token存储起来,在后续向服务器请求服务时就需要带上token,服务端在收到后会先进行验证,验证成功后才提供相应的服务。
Java代码示例
jjwt是java对JWT的封装,我们直接采用jjwt提供的方法来进行JWT的相关操作。
maven dependency:
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.7.0</version>
</dependency>
服务端生成JWT:
public String generateJwt(Map<String,Object> claims){
String jwt = Jwts.builder().setHeaderParam("typ", "JWT").
signWith(SignatureAlgorithm.HS256, SECRET_KEY)
.setClaims(claims).compact();
return jwt;
}
服务端验证JWT
public boolean isJwtValid(String jwt){
try{
Claims claims = Jwts.parser().setSigningKey(SECRET_KEY)
.parseClaimsJws(jwt).getBody();
String value = claims.get("name", String.class);
if("zy".equals(value)){
return true;
}else{
return false;
}
}catch(SignatureException | ExpiredJwtException e){
e.printStackTrace();
return false;
}
}
main方法测试:
public static void main(String[] args) {
JWTDemo jwtdemo = new JWTDemo();
Map<String,Object> claimMap = new HashMap<String,Object>();
claimMap.put("iss", "org.zy");
claimMap.put("exp", "1522658513590");
claimMap.put("name", "zy");
String jwt = jwtdemo.generateJwt(claimMap);
System.out.println("The generated jwt: " + jwt);
System.out.println("the validation of jwt:" + jwtdemo.isJwtValid(jwt));
}
注意事项
- 不要往Payload中存放敏感信息,签名部分只是保证传输的数据不被篡改。
- 尽量给token一个有效期,当然这将涉及到采用何种方法来处理过期token和产生新token的。
- 采用HTTPS,不要采用不安全的HTTP中去传输token。
小结
广义上讲JWT是一个轻量级的标准,狭义上JWT则是一个常用于认证功能的token字符串。 JWT本身很容易,它提供了一种基于token的请求验证机制。但API安全整体是一个比较复杂的问题.