JWT学习小结

JWT的定义

JWT(Json Web Token)是一个轻巧的开放标准(RFC 7519),它定义了一种安全可靠的传输信息的方式,所传输的信息是一个‘小巧’和‘自包含’的JSON对象。作为一个标准,它没有具体的技术实现,不过现在大多数语言平台都按照其标准提供了具体的技术实现。我们在实际的使用过程中,只需要根据具体的技术平台,使用相应的实现库就可以。

使用场景

在实际使用中:JWT主要解决以下两个问题:

  1. 认证: 由于JWT本身‘小巧’和‘自包含’的特点,最常见的用法是通过它来完成基于token的身份认证,服务端在用户首次账号和密码登录成功之后会返回JWT,后续的请求都需要带有这个JWT来作为身份认证的标识。。

  2. 信息安全传输: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));
}

注意事项

  1. 不要往Payload中存放敏感信息,签名部分只是保证传输的数据不被篡改。
  2. 尽量给token一个有效期,当然这将涉及到采用何种方法来处理过期token和产生新token的。
  3. 采用HTTPS,不要采用不安全的HTTP中去传输token。

小结

广义上讲JWT是一个轻量级的标准,狭义上JWT则是一个常用于认证功能的token字符串。 JWT本身很容易,它提供了一种基于token的请求验证机制。但API安全整体是一个比较复杂的问题.

posted @ 2018-04-02 17:46  小苗巴  阅读(211)  评论(0编辑  收藏  举报