JWT(json web token)

简介

JWT,全称是 Json Web Token, 是 JSON 风格轻量级的授权和身份认证规范,可实现无状态、
分布式的 Web 应用授权;官网:https://jwt.io
GitHub 上 jwt 的 java 客户端:https://github.com/jwtk/jjwt
我们最终可以利用 jwt 实现无状态登录

数据格式

JWT 包含三部分数据:

  • Header:头部,通常头部有两部分信息:

    • token 类型:JWT
    • 加密方式:base64(HS256)
  • Payload:载荷,就是有效数据,一般包含下面信息:

    • 用户身份信息(注意,这里因为采用 base64 编码,可解码,因此不要存放敏感信息)
    • 注册声明:如 token 的签发时间,过期时间,签发人等,这部分也会采用 base64 编码,得到第二部分数据。
  • Signature:签名

    • 是整个数据的认证信息。根据前两步的数据,再加上指定的密钥(secret)(不要泄漏,最好周期性更换),通过 base64 编码生成。用于验证整个数据完整和可靠性

在这里插入图片描述

交互流程

在这里插入图片描述
步骤:

  • 1、用户登录
  • 2、服务的认证,通过后根据 secret 生成 token
  • 3、将生成的 token 返回给浏览器
  • 4、用户每次请求携带 token
  • 5、服务端利用秘钥解读 jwt 签名,判断签名有效后,从 Payload 中获取用户信息
  • 6、处理请求,返回响应结果

因为 JWT 签发的 token 中已经包含了用户的身份信息,并且每次请求都会携带,这样服务的
就无需保存用户信息,甚至无需去数据库查询,完全符合了 Rest 的无状态规范

授权中心流程

在这里插入图片描述

优势

  • 易于水平扩展
    • 在 cookie-session 方案中,cookie 内仅包含一个 session 标识符,而诸如用户信息、授权列表等都保存在服务端的 session 中。如果把 session 中的认证信息都保存在JWT 中,在服务端就没有 session 存在的必要了。当服务端水平扩展的时候,就不用处理 session 复制(session replication)/ session 黏连(sticky session)或是引入外部 session 存储了[实际上 spring-session 和 hazelcast 能完美解决这个问题]。
  • 防护 CSRF(跨站请求伪造)攻击
    • 访问某个网站会携带这个域名下的 cookie。所以可能导致攻击。但是我们可以把 jwt放在请求头中发送。
    • Jwt 放在请求头中,就必须把 jwt 保存在 cookie 或者 localStorage 中。保存这里 js就会读写,又会导致 xss 攻击。可以设置 cookie,httponly=true 来防止 xss
  • 安全
    • 只是 base64 编码了,cookie+session 直接将数据保存在服务端,看都看不见,请问哪个更安全?

问题

  • 我们不建议使用 jwt+cookie 代替 session+cookie 机制,jwt 更适合 restful api
  • jwt token 泄露了怎么办?
    • 这个问题可以不考虑,因为 session+cookie 同样泄露了 cookie 的 jsessionid 也会有这个问题
    • 我们可以遵循以下规范减少风险
      • 使用 https 加密应用
      • 返 回 jwt 给 客 户 端 时 设 置 httpOnly=true 并 且 使 用 cookie 而 不 是LocalStorage 存储 jwt,防止 XSS 攻击和 CSRF 攻击
  • secret 如果泄露会导致大面积风险
    • 定期更新
    • Secret 设计可以和用户关联起来,每个用户不一样。防止全用一个 secret
  • 注销和修改密码
    • 传统的 session+cookie 方案用户点击注销,服务端清空 session 即可,因为状态保存在服务端。我们不害怕注销后的假登录
    • Jwt 会有问题。用户如果注销了或者修改密码了。恶意用户还使用之前非法盗取来的 token,可以在不重新登录的情况下继续使用
    • 可以按程度使用如下设计,减少一定的风险
      • 清空客户端的 cookie,这样用户访问时就不会携带 jwt,服务端就认为用户需要重新登录。这是一个典型的假注销,对于用户表现出退出的行为,实际上这个时候携带对应的 jwt 依旧可以访问系统。
      • 清空或修改服务端的用户对应的 secret,这样在用户注销后,jwt 本身不变,但是由于 secret 不存在或改变,则无法完成校验。这也是为什么将secret 设计成和用户相关的原因
      • 借助第三方存储,管理 jwt 的状态,可以以 jwt 为 key,去 redis 校验存在性。但这样,就把无状态的 jwt 硬生生变成了有状态了,违背了 jwt的初衷。实际上这个方案和 session 都差不多了。
      • 修改密码则略微有些不同,假设号被到了,修改密码(是用户密码,不是jwt 的 secret)之后,盗号者在原 jwt 有效期之内依旧可以继续访问系统,所以仅仅清空 cookie 自然是不够的,这时,需要强制性的修改 secret
  • 续签问题
    • 传统的 cookie 续签方案一般都是框架自带的,session 有效期 30 分钟,30分钟内如果有访问,session 有效期被刷新至 30 分钟。而 jwt 本身的 payload之中也有一个 exp 过期时间参数,来代表一个 jwt 的时效性,而 jwt 想延期这个 exp 就有点身不由己了,因为 payload 是参与签名的,一旦过期时间被修改,整个 jwt 串就变了,jwt 的特性天然不支持续签!
    • 可如下解决,但都不是完美方案
      • 每次请求刷新 jwt:简单暴力,性能低下,浪费资源。
      • 只要快要过期的时候刷新 jwt:jwt 最后的几分钟,换新一下。但是如果用户连续操作了 27 分钟,只有最后的 3 分钟没有操作,导致未刷新jwt,就很难受。
      • 完 善 refreshToken : 借 鉴 oauth2 的 设 计 , 返 回 给 客 户 端 一 个refreshToken,允许客户端主动刷新 jwt。这样做,还不如用 oauth2
      • 使用 redis 记录独立的过期时间:jwt 作为 key,在 redis 中保存过期时间,每次使用在 redis 中续期,如果 redis 没有就认为过期。但是这样做,还不如用 session+cookie

总结

  • 在 Web 应用中,别再把 JWT 当做 session 使用,绝大多数情况下,传统的cookie-session 机制工作得更好
  • JWT 适合一次性的命令认证,颁发一个有效期极短的 JWT,即使暴露了危险也很小,由于每次操作都会生成新的 JWT,因此也没必要保存 JWT,真正实现无状态

作者声明

如有问题,欢迎指正!
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,下面是一个简单的基于 JSON Web TokenJWT)的身份验证案例: 1. 安装所需的库 ``` pip install pyjwt ``` 2. 生成JWT Token ```python import jwt import datetime # 定义过期时间 expire_time = datetime.datetime.utcnow() + datetime.timedelta(minutes=30) # 定义payload payload = { 'user_id': '123456', 'username': 'john', 'exp': expire_time } # 生成token jwt_token = jwt.encode(payload, 'secret_key', algorithm='HS256') print(jwt_token) ``` 上述代码中,我们使用了 pyjwt 库来生成 JWT Token。它的 encode() 方法接收三个参数:payload、密钥和算法。payload 是一个字典,包含我们想要在 Token 中存储的信息,例如用户ID、用户名、过期时间等等。密钥是一个字符串,用于加密 Token。算法是指用于加密 Token 的算法,这里我们选择了 HS256。 3. 验证JWT Token ```python import jwt # 定义Token jwt_token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoiMTIzNDU2IiwidXNlcm5hbWUiOiJqb2huIiwiZXhwIjoxNjMxMzEwMDUzfQ.XsDEjcd7jH8qC-6pZlWjZaFvDz-pT8NvQYrWb8I3-5c' # 验证Token try: decoded_token = jwt.decode(jwt_token, 'secret_key', algorithms=['HS256']) print(decoded_token) except jwt.ExpiredSignatureError: print('Token已过期') except jwt.InvalidTokenError: print('无效的Token') ``` 上述代码中,我们使用了 pyjwt 库的 decode() 方法来验证 Token。它接收三个参数:Token、密钥和算法。如果 Token 有效,则返回包含信息的字典。如果 Token 过期或无效,则会引发 jwt.ExpiredSignatureError 或 jwt.InvalidTokenError 异常。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值