jwt是保存在客户端的字符串,里面携带了用户的一些基本信息,每次请求时候由客户端携带jwt token去后端请求接口。
jwt认证授权过程
一般jwt认证授权过程可以描述如下:
- 用户登录通过系统的认证后,系统颁发jwt令牌给该用户,用户将该jwt存储起来,可存放在cookie,header,或者localstorage等;
- 用户发送请求,请求携带上系统颁发的jwt去请求服务;
- 服务端解析该jwt令牌,若是解析且验证通过,将解析后的用户信息存储在ThreadLocal容器,作为该请求的一个识别体;否则将提示授权失败。
- 在jwt有效期内,用户便可以通过该令牌进行服务资源的请求。
jwt一旦颁发就会一直有效,直到有效期过后;为了更好的利用http无状态特性,服务端也没有对jwt进行缓存或者进行会话管理,这样在此期间用户退出系统后,jwt的有效期还没有到,其他用户便可以窃取该token进行服务资源的请求;
区别于cookie-session的鉴权方式,在jwt鉴权中,仍然存在一定的身份认证漏洞;这是很危险的一个过程,也是一个缺陷。
对比cookie-session
session-cookie,我们暂且不考虑分布式情况下session共享和其他可能的的安全攻击;
jwt客户端持有,sessionId服务端保存;这种方式让http无状态的请求变成了有状态的。
用户通过cookie携带sessionId,去请求服务资源,后台服务资源根据sessionId来区分用户,请求来源,是否授权等;
这种虽然是有状态的,但是可以由服务端去控制用户的权限;
借鉴:jwt是否也能够用这种方式解决这个jwt管理工作呢?
当然可以,除了在客户端保持jwt,在服务端也可维持一份jwt,
so问题来了,那选取jwt是为了利用jwt的无状态,将jwt保存在服务端,那又跟session-cookie有什么区别呢?
区别当然是有的:
-
session存储数据相对较多,要在服务端维持所有的sessionId表,用户同时访问会占用更高的处理内存;
当然你可以设置个外置缓存,如redis来解决内存消耗,顺便还可以解决分布式session共享问题;相比于session来说,我们可以在服务端维持一份jwt黑白名单,通过jwt过期时间能够很好地控制jwt在服务端保持的总量;
比如某些由于主动退出或者主动作废的jwt,便可以通过redis进行维护,redis将jwt中的jti字段进行存储和exp字段进行过期时间的维护;在结合系统可以设置jwt过期时间短一点,这样就能很好的控制jwt黑白名单的增长。 -
jwt本身提供了jti字段,这个默认字段含义是JWT ID,这个按照官方说明,就是说这个不建议将jwt字符串整体存储在服务端保持,但可以短时间去缓存这个jwt;
可以使用jti字段(jti是有效载荷里面的一个预设字段)
RFC 7591中的定义:The “jti” (JWT ID) claim provides a unique identifier for the JWT.
The identifier value MUST be assigned in a manner that ensures that there is a negligible probability that
the same value will be accidentally assigned to a different data object;
if the application uses multiple issuers, collisions MUST be prevented among values produced by different issuers as well.
The “jti” claim can be used to prevent the JWT from being replayed.
The “jti” value is a case- sensitive string. Use of this claim is OPTIONAL.无状态请求可以简单理解为:前后请求无关联无上下文,服务端处理每个请求的全部信息必须只来自该请求,以及其他服务端保持的可以被所有请求共用的公共信息(外部存储介质),如redis里面的公共信息。
因此这并不违反无状态。
jwt解决方案优化
添加黑名单机制:
- 每次生成颁发jwt时候,附加生成一个jti字段来标识每个jwt;
- 使用redis来缓存jwt,存储结构选取string类型,设置key格式(suggestion):jwt:user_id:jti 例如:jwt:17788822333:dakhawadjfaihf
- 为了防止redis挂掉中断,可以加入一个数据表进行临时替代品,id可取jti字段值。
- 用户主动退出时候,我们在登出逻辑中将该jwt放入黑名单中,设置失效时间即为jwt过期时间。(jwt一旦颁发变不可更改,这样设置时间是经过考量的,具体略;
- 每次请求校验时候,除了校验jwt本身,还要去黑名单中去查询一下,如果有,即使解析通过也不得授权;
补充
后续将对jwt payload预设内容做出如下优化
预设字段 | 说明 | 描述 |
---|---|---|
iss | 签发者 | 存放签发系统或其子系统的标识,用来后续区分颁发者信息做不同处理 |
sub | 所面向的用户 | 存放用户的唯一标识信息,如UserID/UserName/UserPhone等 |
aud | 接收的受众 | 存放设备标识,如ip,登录设备是IOS还是Android等 |
jti | jwt id | 存放jwt令牌的唯一标识,可作为数据库主键id,redis缓存key |
exp | 过期时间 | 标识令牌失效时间,Unix时间戳(s) |
iat | 签发时间 | 标识令牌颁发时间,Unix时间戳(s) |
nbf | 之前时间不可用 | 标识令牌生效时间,Unix时间戳(s) |
all done