互联网服务离不开用户认证,一般流程如下!:
1.用户向服务器发送用户名和密码
2.服务器验证通过后,在当前对话(session)里面保存相关数据,比如用户角色、登录时间等
3.服务器向用户返回一个session_id,写入用户的 Cookie
4.用户随后的每一次请求,都会通过Cookie,将session_id传回服务器
5.服务器收到session_id,找到前期保存的数据,由此得知用户的身份
但使用这种方法,首先是扩展性不好,如果是单机服务那问题不大,如果是服务器集群的话,就需要实现session数据共享,使得每台服务器都能够读取session
这样的话就有一种解决方法是将session数据持久化,写入数据库或者别的持久层,各种服务收到请求后,都去向持久层请求得到数据,但是如果持久层挂掉了就会单点失败
另外一种就是将session由客户端进行保存,减少服务端的负载,每次请求都携带返回给服务器,JWT就是这种方法的一个方案
一般在鉴权或者信息交换的过程中使用,主要是Authorization时使用,一旦用户登录,后续每个请求都将包含JWT,允许用户访问该令牌允许的路由、服务和资源。单点登录是现在广泛使用的JWT的一个特性,因为开销很小
一个jwt看起来就是xxxx.yyyy.zzzz
Header由两部分组成,token的类型("JWT")和算法名称比如(SHA256或RSA等等)
golang{ 'alg':"HS256", 'typ':"JWT" }
用base64对其进行编码就得到第一部分
Payload包含声明(要求),声明是关于实体(通常是用户)和其他数据的声明,声明有三种类型:registered,public和private
Registered claims
是一些官方推荐的使用的字段,提供了一些有用的信息,iss(issuer):签发人
exp(expiration time):注册时间 sub(subject):主题
aud(audience):受众 nbf(Not Before):生效时间 iat(Issued At):签发时间
jti(JWT ID):编号
Public claims
在使用JWT时可以被自定义
Private claims
用户自定义的一些信息
Signature(签名)
对第一部分和第二部分进行签名使用,用来验证是否是我们服务器发起的token,secert是我们的密钥
具体的签名过程则之后的文章再讲解,这里先跳过
那一个JWT验证的过程是怎么样的?
主要包括两个流程,一个是签名验证,另外一个是payload里面各个标准claim的验证
接收到一个JWT时,首先对完整性进行验证,防止被串改,因为签名是加入了私钥进行计算的,可以将签名段看为一个原始数据,然后对接收到的header解码,就能知道JWT用的是什么算法做的签名,然后再利用这个算法,对header和payload做一次签名,并比较两个签名是否完全相同,若相同则验证成功
之后则对payload的claim验证
iss:如果签发时claim是'a.com',验证时不是的话则失败
sub:如果与签发时不同则验证失败
除了给定的registered claims验证以外,还需要开发者对自定义的claims进行验证
即jwt不会注销,在过期时间之前它是始终有效的,比如用户点击了注销按钮,即结束了这个会话,客户端正常会清空它存储的token,但是实际上这个token在用户注销后还是有用的,还可以被攻击者窃取后进行使用,直到过期时间结束
那这时候就可以利用redis来撤销JWT产生的令牌,当用户点击注销后,且令牌在redis存储的时间与令牌在jwt的有效时间相同,当有效时间到了以后,令牌会自动被redis删除,然后每次先检验令牌在redis中是否存储,然后点击注销的时候也会发送请求注销令牌
本文作者:Malyue
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!