拒绝长篇大论,快速使用JWT(json web token)

拒绝搬砖,拒绝长篇大论

什么是JWT

JWT全称json web token。是一个非常轻巧的规范。这个规范允许我们使用JWT在用户和服务器之间传递安全可靠的信息。它其实就是平常用的token认证机制,只不过它有它自己的一套规范。

根据JWT规范生成的token字符串由三部分组成:头部、载荷、签名。

示例(中间由.隔开):

20190627094916303.png

相较于普通的token生成方式,JWT生成的token由上面三部分组成(每部分由Base64URL编码完成),而且JWT有相应的解码规则,可以从token字符串里面解析出有用的信息,再进行相应的校验。在使用过程,我们可以和普通的token一样,用在url中、请求头中或者cookie中。

详细构成

1、头部(header)

JWT需要一个头部,头部用于描述关于该JWT的最基本的信息,例如其类型以及签名所用的算法等。这也可以被表示成一个JSON对象。

{“typ”: “JWT”,”alg”: “HS256”}

在头部指明了签名算法是HS256算法。

进行Base64URL编码之后:

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9

2、载荷(Payload)

载荷就是此JWT生成的token所携带的一些信息(比如用户信息)和设置等。载荷也可以表示为json对象的key-value形式,其规则如下:

--支持自定义的key-value(key的命名应该避免JWT定义的);

--iss: 该JWT的签发者,可选的;

--sub: 该JWT所面向的用户,可选的;

--aud: 接收该JWT的一方,可选的;

--exp(expires): 什么时候过期,这里是一个Unix时间戳,可选的;

--iat(issued at): 在什么时候签发的(UNIX时间),可选的;

--nbf (Not Before):如果当前时间在nbf里的时间之前,则Token不被接受;一般都会留一些余地,比如几分钟,可选的。

一个简单的载荷示例:

{

“userId” : 123456, #用户id,表明用户

“iat” : 1356999524, #token发布时间

“exp” : 1556999524, #token过期时间

}

 将其进行Base64URL编码后:

eyJlbWFpbCI6IjQ5NDQ1NDk4MkBxcS5jb20ifQ

3、签名(Signature) 

将上面的两个编码后的字符串都用 . 连接在一起(头部在前),就形成了:

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJlbWFpbCI6IjQ5NDQ1NDk4MkBxcS5jb20ifQ

最后,我们将上面拼接完的字符串用HS256算法进行加密。在加密的时候,我们还需要提供一个密钥(secret)。就形成了:

_lIhl8oPiCIDssYFpnvTgYA-yTLj4gsENuvohSN5ZV0

最后将这一部分签名也拼接在被签名的字符串后面,我们就得到了完整的JWT:

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJlbWFpbCI6IjQ5NDQ1NDk4MkBxcS5jb20ifQ._lIhl8oPiCIDssYFpnvTgYA-yTLj4gsENuvohSN5ZV0

然后,我们就可以快乐的使用我们的JWT了,比如

http://localhost:8080/user/list?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJlbWFpbCI6IjQ5NDQ1NDk4MkBxcS5jb20ifQ._lIhl8oPiCIDssYFpnvTgYA-yTLj4gsENuvohSN5ZV0

快速开始

引入jar包

<dependency>
      <groupId>com.auth0</groupId>
      <artifactId>java-jwt</artifactId>
      <version>3.4.0</version>
</dependency>

生成JWT

 先创建一个用户实体类,将其信息携带在JWT中。

public class User {

	private Integer id;
	
	private String userName;
	
	private String passWord;

	//省略get/set
}
User user = new User(1, "zhangsan", "123456");
//将user对象序列化
String user_json = JSONObject.toJSONString(user);
//token发布时间
long iat_time = System.currentTimeMillis();
//token失效时间  2小时后失效
long exp_time = iat_time + (2*60*60*1000);
String token = JWT.create()
				//将用户信息加入载荷
				  .withClaim("user", user_json)
				//将token发布时间加入载荷
					.withIssuedAt(new Date(iat_time))
				//将token失效时间加入载荷
					.withExpiresAt(new Date(exp_time))
				//使用秘钥为123456 进行HS256加密
					.sign(Algorithm.HMAC256("123456"));
System.out.println(token);

生成的token

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1NjE2MTIxODYsInVzZXIiOiJ7XCJpZFwiOjEsXCJwYXNzV29yZFwiOlwiMTIzNDU2XCIsXCJ1c2VyTmFtZVwiOlwiemhhbmdzYW5cIn0iLCJpYXQiOjE1NjE2MDQ5ODZ9.zGgu0F_o_1vBvVDYGRedwP2FlnbekAL39nzaCQ-Yf6o

解码JWT

String token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1NjE2MTIxODYsInVzZXIiOiJ7XCJpZFwiOjEsXCJwYXNzV29yZFwiOlwiMTIzNDU2XCIsXCJ1c2VyTmFtZVwiOlwiemhhbmdzYW5cIn0iLCJpYXQiOjE1NjE2MDQ5ODZ9.zGgu0F_o_1vBvVDYGRedwP2FlnbekAL39nzaCQ-Yf6o";
		try {
			//获取user信息 
			String user_json = JWT.decode(token).getClaim("user").asString();
			System.out.println(user_json);
			//获取发布时间
			Date iat_time = JWT.decode(token).getIssuedAt();
			SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
			System.out.println(df.format(iat_time));
			//获取失效时间
			Date exp_time = JWT.decode(token).getExpiresAt();
			System.out.println(df.format(exp_time));
			//获取整个载荷Base64URL
			String payload = JWT.decode(token).getPayload();
			System.out.println(payload);
			//获取整个头部Base64URL
			String header = JWT.decode(token).getHeader();
			System.out.println(header);
			//获取整个签名的Base64URL
			String signature = JWT.decode(token).getSignature();
			System.out.println(signature);
			//头部详细信息
			String type = JWT.decode(token).getType();
			System.out.println(type);
			String algorithm = JWT.decode(token).getAlgorithm();
			System.out.println(algorithm);
			
		} catch (Exception e) {
			System.out.println("----token是否符合JWT规范----" + e.getMessage());
			System.out.println("----避免空指针----" + e.getMessage());
		}
		//根据秘钥验证  生成JWT的Signature用的secret是123456   这种解码方式可以防止token伪造
		JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256("123456")).build();
		String result = jwtVerifier.verify(token).getClaim("user").asString();
		System.out.println(result);
{"id":1,"passWord":"123456","userName":"zhangsan"}
2019-06-27 11:09:46
2019-06-27 13:09:46
eyJleHAiOjE1NjE2MTIxODYsInVzZXIiOiJ7XCJpZFwiOjEsXCJwYXNzV29yZFwiOlwiMTIzNDU2XCIsXCJ1c2VyTmFtZVwiOlwiemhhbmdzYW5cIn0iLCJpYXQiOjE1NjE2MDQ5ODZ9
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
zGgu0F_o_1vBvVDYGRedwP2FlnbekAL39nzaCQ-Yf6o
JWT
HS256
{"id":1,"passWord":"123456","userName":"zhangsan"}

生产环境

我们可以在校验用户名密码后返回token,然后在拦截器中进行token校验。

总结

1、JWT的载荷可以携带任何信息,所以可以有效避免频繁的去数据库或者缓存中获取有效信息进行校验;

2、善用签名部分的秘钥(Secret),避免全局单一secret(虽然secret是保存在服务端,不参与数据传输)。

每个用户一个secret,可以完全解决用户退出和修改密码问题,用户退出,清除相关的secret,没有secret无法解码。

在上面JWT生成的时候可以明显看到,JWT的发布时间和失效时间是参与签名的,所以这个无法修改。

还是只能用secret去解决签名的续签问题,那么就需要将JWT的生命周期设置为永久有效,完全交给secret去控制。

除此之外,更换新的token去延时也是可以的,最简单粗暴。到底使用哪种方式,就需要做取舍了。

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值