在上篇介绍session和cookie的时候,深入浅出的解析session和cookie,文章最后,提到了jwt的token认证, 那么这篇文章就详细说一下jwt.

组成

jwt实际上是一个字符串,三部分组成: 头部,载荷和签名

我先摘取了我们项目中的jwt,大家看一下内容:

JSON Web Token_服务器

左边的是token, 右边的是它的解析

头部

我们可以看到头部的解析,头部用来描述关于该jwt的基本信息,其类型和签名所用的算法,也可以被表示成一个json对象. 很明显我们项目中使用的HS512签名算法, 对它要进行Base64编码,之后的字符串就编程了jwt的header. 也就是左侧红色字符串.

java代码:

JSON Web Token_服务器_02

载荷

jti: JWT ID当前token唯一标识

sub: 该jwt所面向用户

iat: token创建时间, 时间戳

aud: 接收该jwt的一方

exp: token的生命周期

第一个图, 右侧紫色的,就是载荷的内容, 我们将左侧的json进行base64编码后,得到左侧紫色的字符串,这个字符串称为jwt的PayLoad

java代码:

JSON Web Token_redis_03

注意: base64只是一种编码,可以翻译回原来的样子的!

签名

JSON Web Token_redis_04

继续看这张图, 上面我们讲了头部和载荷,都已经生成了字符串,那么我们把它用. 连接起来,就是上图圈起来的内容了. 我们把这个内容用HS512算法进行签名, 加密的时候,提供一个密钥(secret).然后用这个密钥,可以得到加密后的内容,也就是上图中蓝色字符串内容.这个就是签名

现在头部, 载荷, 和签名都有了. 把这三个内容,用.连接起来. 整个的就是一个完整的jwt了

如何保证安全性

重点就是签名这里了. 不同的加密算法,对于不同的输入,其输出结果是不一样的.如果把头部或者载荷进行了修改,那么新生成的签名,就合原来的不一样了.而且上面也提到了,服务器加密的时候, 提供了一个密钥. 如果不知道这个密钥,算出来的签名也是不一样的.

服务器接收的时候,会对其头部还有载荷,再以相同的方法,计算出签名,如果自己计算出的,和接收到的签名不一致,就证明token不对了. 需要拒绝. 服务器拿到的加密算法, 是头部那里用字段指明了.


  • 敏感信息,还是不要放在载荷里面了. 因为载荷用base64编码, 是可以解码获取的.
  • 不被泄露

    • https加密. 返回jwt时设置httpOnly=true. 如果把jwt字符串作为请求cookie的一部分返回给用户的,所以使用httpOnly可以防止cookie被js读取. 因为如果想要获取cookie劫持的话, 浏览器中的document对象中存储的cookie信息.利用js可以把这个里面的cookie取出来,如果使用httpOnly.就是相当于设置cookie时接收一个参数,如果设置这个参数了. 浏览器的document对象就看不到cookie了. 浏览器浏览网页是不受影响的.但是httponly不是万能的, 也不能解决xss攻击问题, 只是提高了攻击门槛了.
    • 放弃localStorage存储jwt(我们的漏洞…)


过期问题


  • 每次请求刷新jwt
    • 每次请求的时候,都修改exp,然后再把新的jwt给客户端..

  • 快过期的时候刷新jwt
    • 对于第一个方案的改进, 可以减少这种性能问题,但是可能有这么一个情况: 过期时间是30分组,然后本来一直在连续操作, 恰好设定的最后几分钟没有操作. 重新登录吧..

  • redis记录(我们项目用的方式)
    • redis本身也是有过期时间的.所以只要找不到key,就认为过期了. 如果有新的访问, 就更新一下时间戳


注销


  • 由于我们的jwt是放在localstorage,然后jwt是用redis存储的,所以采用页面缓存清空+redis删除, 即可实现.
  • 如果是放到cookie中, 仅仅清空客户端cookie.用户访问不带jwt,服务端也就认为用户需要重新登录了. 但是这个算是假注销吧.
  • 我们在说签名的时候, 有提到一个密钥, 如果把这个密钥该了. 也就报401了.