JWT,是什么,做什么用,怎么用,为什么要用:
全称:Json Web Token
是什么:
是一个开发标准,定义了一个紧凑的,自包含的方式,
干什么用的:
用于各方之间以json对象安全地传输信息,传输中还可以对数据进行加密,签名处理。
怎么用:
一。授权,用户登录后,后续每个请求都包含JWT,从而允许用户访问该令牌允许的 路由,服务和资源,,单点登录是当今广泛使用JWT的一项功能,有开销少,可在不同域中使用的功能
二。信息交换,JSON Web令牌是在各方之间安全地传输信息的一种好方式,所以可以对JWT进行签名,但要确保发件人就是本人,此外,签名是使用标头和有效负载计算的,因此还可以验证内容是否被篡改。
为什么要用:
因为HTTP协议是无状态的,如果我们已近认证了一个用户,那么他下一次发起请求时,服务器不能识别我们,我们就必须再次认证。
为了避免这种情况的发生我们就可以使用;
Cookie+Session的方式,当用户登录成功后,我们把用户的信息保存到我们的Session中,然后把该Session以Cookie的形式保存到客户端浏览器上,此后,该用户的所有请求都会把包含Sessionld的Cookie带回给服务器,服务器获取到Sessionld,通过改Sessionld得到Session对象,这样就能获取到该用户登录时的信息了
但这种方式也有缺点:
1.每次用户认证后,服务器都要创建一个Session对象保存用户信息,用户越多,服务器保存的session也越多,服务器的内存开销也会越来越大
2.Sessionld是基于Cookie来储存的,如果Cookie被截获,用户很容易受到跨站请求伪造攻击(CSRF)
3.当我们扩展应用,数据被多个移动设备使用时,我们必须考虑跨资源共享的问题,当使用AJAX调用 从另一个域名下获取资源时,可能会出现禁止请求的问题(CORS)
4.在分布式集群系统中,我们的后端应用是多节点部署,而session是储存在节点服务器上,这个时候又要实现Session的共享机制,所以不方便集群应用。
我们还可以使用:
基于iJWT的认证方式:
- 前端通过web表单将自己的用户名和密码发送给后端的接口,这一过程一般就是一个HTTP POST请求.在实际开发中,我们可以通过SSL加密的传输(Https协议),从而避免敏感信息被嗅探.
- 后端核对用户名和密码成功之后,将用户的id等其他信息作为JWT的payload(负载),将其与头部分别进行base64编码拼接之后签名,形成一个JWT(token).形成的JWT就是一个形同:aaa.bbb.ccc的字符串.
- 后端服务将JWT字符串作为登录成功的返回结果返回给前端,前端可以将返回的结果保存在LocalStore或sessionStore上,退出登录时,前端删除保存的JWT即可.
- 前端在每次请求时,将JWT放入HTTP的请求头中的Authorization中(解决XSS和XSRF问题)
- 后端验证JWT的有效性,例如:检查签名是否正确,检查Token是否过期,检查Token的接收方是否是自己的(可选)
- 验证通过后,后端使用JWT中包含的用户信息进行其他逻辑操作,返回相应的结果.
- 如果验证不通过, 响应错误信息给用户.
JWT与Session的差异:
相同点是,它们都是存储用户信息;然而,Session是在服务器端的,而JWT是在客户端的。
Session方式存储用户信息的最大问题在于要占用大量服务器内存,增加服务器的开销。
而JWT方式将用户状态分散到了客户端中,可以明显减轻服务端的内存压力。
Session的状态是存储在服务器端,客户端只有session id;而Token的状态是存储在客户端。
JWT的优势:
1.简洁:可以通过URL,POST参数或Http请求头发送,因为数据量小,传输速度也快;
2.自包含:负载包含了用户所需要的数据,避免多次查询数据库
3.因为Token是以JSON加密的形式保存在客户端,所以JWT是跨语言的,原则上任何语言的Web项目都支持
4.不需要再服务端保存会话信息,特别适用于分布式微服务.
JWT的结构:
JSON Web令牌以紧凑的形式由三部分组成,这些部分由点(.)分隔,分别是:
- 头部(header)
- 载荷 (payload)
- 签名(signature)
JWT通常如下所示: xxxxxx.yyyyyy.zzzzz
示例:
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJVc2VySWQiOjEyMywiVXNlck5hbWUiOiJhZG1pbiJ9.Qjw1epD5P6p4Yy2yju3-fkq28PddznqRj3ESfALQy_U
头部:
由两个部分组成:
令牌类型:JWT
签名算法:(如:HMAC,SHA256或RSA)
载荷:载荷中放置了 token 的一些基本信息
用来理解token,同时还可以包含一些自定义的信息,用户信息交换。这些信息包含:
- 标准中注册声明
- 公共的声明
- 私有的声明
标准中注册声明(建议不强制使用)
公共的声明
在使用 JWT 时可以额外定义的载荷。为了避免冲突,应该使用 IANA JSON Web Token Registry 中定义好的,或者给额外载荷加上类似命名空间的唯一标识。
公共的声明可以添加任何的信息,一般添加用户的相关信息或其它业务需要的必要信息,但不建议添加敏感信息,因为该部分在客户端可解密;
私有的声明
在信息交互的双方之间约定好的,既不是预定义载荷也不是公有载荷的一类载荷。这一类载荷可能会发生冲突,所以应该谨慎使用。
比如我们定义了一个payload:
{ "sub": "1234567890", "name": "John Doe", "admin": true }
然后将其base64url加密,得到jwt的一部分
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9
请注意,对于已签名的令牌,此信息尽管可以防止篡改,但任何人都可以读取。除非将其加密,否则请勿将机密信息放入JWT的有效负载或报头元素中。
JWT的总结
JWT的优点:
- 因为json的通用性,所以JWT是可以跨语言支持的,像C#,JavaScript,NodeJS,PHP等许多语言都可以使用
- 因为由了payload部分,所以JWT可以在自身存储一些其它业务逻辑所必要的非敏感信息
- 便于传输,jwt的构成非常简单,字节占用很小,所以它是非常便于传输的
- 它不需要在服务端保存会话信息,所以它易于应用的扩展安全相关
- 不应该在jwt的payload部分存储敏感信息,因为该部分是客户端可解密的部分
- 保护好secret私钥。该私钥非常重要
- 如果可以,请使用https协议
JWT的java实现
1.导入依赖
io.jsonwebtoken
jjwt
0.9.1
代码实现:
@Test
public void testCreateToken(){
Calendar instance = Calendar.getInstance();
instance.add(Calendar.SECOND,90);
JwtBuilder builder = Jwts.builder().setId("888").setSubject("zhangsan")
.setIssuedAt(new Date())//设置签发时间
.signWith(SignatureAlgorithm.HS256, "feisi")//使用HS256生成token,签名秘钥 则是feisi,唯一密钥的话可以保存在服务端。
.setExpiration(instance.getTime());//设置过期时间
System.out.println(builder.compact());
}
生成的token内容:
eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiI4ODgiLCJzdWIiOiJ6aGFuZ3NhbiIsImlhdCI6MTY5MTI0NTMzMSwiZXhwIjoxNjkxMjQ1NDIxfQ._aKLZld6rlf-Vt6xkdihFFJLpptbW4exvPBAttA2g4I
再次运行,会发现每次运行的结果是不一样的,因为我们的载荷中包含了时间
3.根据token和签名解析数据
我们刚才已经创建了token ,在web应用中这个操作是由服务端进行然后发给客户端,客户端在下次向服务端发送请求时需要携带这个token(这就好像是拿着一张门票一样),那服务端接到这个token应该解析出token中的信息(例如用户id),根据这些信息查询数据库返回相应的结果。
@Test
public void testProcessToke(){
String token ="eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiI4ODgiLCJzdWIiOiJ6aGFuZ3NhbiIsImlhdCI6MTY5MTI0NTMzMSwiZXhwIjoxNjkxMjQ1NDIxfQ._aKLZld6rlf-Vt6xkdihFFJLpptbW4exvPBAttA2g4I";
Claims claims =
Jwts.parser().setSigningKey("feisi").parseClaimsJws(token).getBody();
System.out.println("id:"+claims.getId());
System.out.println("subject:"+claims.getSubject());
System.out.println("IssuedAt:"+claims.getIssuedAt());
System.out.println("expiration:"+claims.getExpiration());
}
4.常见的异常信息:
- ClaimJwtException:在验证JWT声明失败后抛出
- ExpiredJwtException:表示JWT在过期后被接受,必须被拒绝
- MalformedJwtException:当JWT未正确构造并且应该被拒绝时抛出
- PrematureJwtException:表示JWT在被允许访问之前被接受,必须被拒绝
- SignatureException:表示计算签名或验证JWT的现有签名失败
- UnsupportedJwtException:在接收到与应用程序预期格式不匹配的特定格式/配置的JWT时抛出。例如,如果在应用程序需要加密签名的声明JWS时解析无符号明文JWT,则会抛出此异常
- JJWT使用了许多其他Exception类。它们都可以在JJWT源代码中的io.jsonwebtoken包中找到。