【认证与授权】JWT
🎫【认证与授权】JWT
2023-4-14
| 2023-12-18
0  |  0 分钟
type
status
date
slug
summary
tags
category
icon
password
Sub-item
Last edited time
Dec 18, 2023 01:45 AM
Parent item
领域
JWTJSON 结构的 token。token是访问资源的凭据。例如当你调用Google API,需要带上有效 token 来表明你请求的合法性。token 是 Google 给你的,这代表 Google 给你的授权使得你有能力访问 API 背后的资源。
用于调用 API 的 token 我们称为 access token,用于更新access token的token叫做refresh token。一旦 access token 过期,你就可以通过 refresh token 再次请求 access token。如果 refesh token 也过期了,就需要用户重新登陆授权。

基于session认证所显露的问题

Session: 每个用户经过我们的应用认证之后,我们的应用都要在服务端做一次记录,以方便用户下次请求的鉴别,通常而言session都是保存在内存中,而随着认证用户的增多,服务端的开销会明显增大。
扩展性: 用户认证之后,服务端做认证记录,如果认证的记录被保存在内存中的话,这意味着用户下次请求还必须要请求在这台服务器上,这样才能拿到授权的资源,这样在分布式的应用上,相应的限制了负载均衡器的能力。这也意味着限制了应用的扩展能力。
CSRF: 因为是基于cookie来进行用户识别的, cookie如果被截获,用户就会很容易受到跨站请求伪造的攻击。

为什么需要JWT

  • 无状态认证,类似邮件激活中的一次性验证
  • 有状态认证,类似session
  • 同时支持浏览器类客户端和非浏览器类客户端
  • 支持跨域访问: Cookie是不允许垮域访问的,Token机制传输的用户认证信息通过HTTP头传输。
  • 无状态(也称:服务端可扩展行):Token机制在服务端不需要存储session信息,因为Token 自身包含了所有登录用户的信息,只需要在客户端的cookie或本地介质存储状态信息。
  • 更适用CDN: 可以通过内容分发网络请求你服务端的所有资料(如:javascript,HTML,图片等),而你的服务端只要提供API即可。
  • 去耦: 不需要绑定到一个特定的身份验证方案。Token可以在任何地方生成,只要在你的API被调用的时候,你可以进行Token生成调用即可。
  • 更适用于移动应用: 当你的客户端是一个原生平台(iOS, Android,Windows 8等)时,Cookie是不被支持的(你需要通过Cookie容器进行处理),这时采用Token认证机制就会简单得多。
  • CSRF:因为不再依赖于Cookie,所以你就不需要考虑对CSRF(跨站请求伪造)的防范。
  • 性能: 一次网络往返时间(通过数据库查询session信息)总比做一次HMACSHA256计算 的Token验证和解析要费时得多。
  • 不需要为登录页面做特殊处理: 如果你使用Protractor 做功能测试的时候,不再需要为登录页面做特殊处理。
  • 基于标准化:你的API可以采用标准化的 JSON Web Token (JWT). 这个标准已经存在多个后端库(.NET, Ruby, Java,Python, PHP)和多家公司的支持(如:Firebase,Google, Microsoft)。
  • 因为json的通用性,所以JWT是可以进行跨语言支持的,像JAVA,JavaScript,NodeJS,PHP等很多语言都可以使用。
  • 因为有了payload部分,所以JWT可以在自身存储一些其他业务逻辑所必要的非敏感信息。
  • 便于传输,jwt的构成非常简单,字节占用很小,所以它是非常便于传输的。
  • 它不需要在服务端保存会话信息, 所以它易于应用的扩展。

JWT 需要注意的点[8]

  1. JWT 默认是不加密,但也是可以加密的。生成原始 Token 以后,可以用密钥再加密一次。
  1. JWT 不加密的情况下,不能将秘密数据写入 JWT。
  1. JWT 不仅可以用于认证,也可以用于交换信息。有效使用 JWT,可以降低服务器查询数据库的次数。
  1. JWT 的最大缺点是,由于服务器不保存 session 状态,因此无法在使用过程中废止某个 token,或者更改 token 的权限。也就是说,一旦 JWT 签发了,在到期之前就会始终有效,除非服务器部署额外的逻辑。
  1. JWT 本身包含了认证信息,一旦泄露,任何人都可以获得该令牌的所有权限。为了减少盗用,JWT 的有效期应该设置得比较短。对于一些比较重要的权限,使用时应该再次对用户进行认证。
  1. 为了减少盗用,JWT 不应该使用 HTTP 协议明码传输,要使用 HTTPS 协议传输。

Refresh Token

为什么我们需要 refresh token?
这样的处理是为了职责的分离:refresh token 负责身份认证access token 负责请求资源。虽然 refresh token 和 access token 都由 IdP 发出,但是 access token 还要和 SP 进行数据交换,如果公用的话这样就会有身份泄露的可能。并且 IdP 和 SP 可能是完全不同的服务提供的。而在第一小节中我们之所以没有这样的顾虑是因为 IdP 和 SP 都是 Google
  • Refresh 通常比 Access Token "存活"的时间长, 例如一个月或两个月.
  • 我们将 Refresh Token 存放在数据库, 每次客户端需要更新 Access Token 我们就验证它. 它是一个随机生成的字符串(例如: Lxd6bj7w33GEX1GOSgzCNZWNSMskaUmPwgG6uM)

Refresh Token 更新

  1. 客户端通过登录信息/密码对进行验证.
  1. 如果成功, 服务器创建新的 Access Token 和 Refresh Token,
      • Refresh Token 和 他的生命周期一起存放到数据库.
  1. 服务器以这个形式响应给客户端:
    1. 客户端保存这些信息.
    1. 每次请求前, 客户端检测 Access Token 是否过期. 如果未过期, 请求携带 token 一起发送.
    1. 为了刷新token, 客户端发送 Refresh Token 到指定的 api 路径(例如: /v1/account/refresh-token).
    1. 服务器通过查询数据库, 对比发送的 refresh token 是否正确, 并且检测是否过期.
    1. 如果 refresh token 过期, 或者数据库不存在此 token, 则取消认证并向客户端返回 401 错误.
    1. 如果 refresh token 存在并且未过期, 则创建新的 access token 并更新 refresh token, 以 步骤3 的格式返回给客户端.
    1. 客户端现在可以通过新的 access token 继续进行请求操作.

    JWT处理过程

    1. 用户向认证服务器提交用户名和密码,认证服务器也可以和应用服务器部署在一起,但往往是独立的居多;
    1. 认证服务器校验用户名和密码组合,然后创建一个JWT token,token的Payload里面包含用户的身份信息,以及过期时间戳;
    1. 认证服务器使用密钥对Header和Payload进行签名,然后发送给客户端;
    1. 客户端获取到经过签名的JWT token,然后在之后的每个HTTP请求中附带着发送给应用服务器。经过签名的JWT就像一个临时的用户凭证,代替了用户名和密码组合,之后都是JWT token和应用服务器打交道了;
    1. 应用服务器检查JWT签名,确认Payload确实是由密钥拥有者签过名的;
    1. Payload身份信息代表了某个用户;
    1. 只有认证服务器拥有私钥,并且认证服务器只把token发给提供了正确密码的用户;
    1. 因此应用服务器可以认为这个token是由认证服务器颁发的也是安全的,因为该用户具有了正确的密码;
    1. 应用服务器继续完成HTTP请求,并认为这些请求确实属于这个用户;

    JWT token组成

    一个JWT token实际上就是一个字符串,它由三部分组成,通过.分割:
    notion image

    头部header

    签名算法有如下,来自 golang-jwt/jwt/v4
    如果是非对称签名算法, 那么secretkey是私钥
    然后将头部序列化并进行base64编码,得到一个字符串:

    载荷payload

    签名sign

    签名是为了防止header和payload在传输过程中被劫持篡改。因为签名时加入了secret,可以保证签名无法被伪造。
    签名算法如下:
    • 接口服务端 拿到 密钥,假设为 secret
    • header 进行 base64 编码,假设结果为 headerStr
    • payload 进行 base64 编码,假设结果为 payloadStr
    • headerStrpayloadStr. 字符 拼装起来成为字符 data
    • datasecret 作为参数,使用 哈希算法 计算出 签名
    得到我们加密后的内容:
    在服务器端完成secret保存和jwt的签发,secret就是你服务端的私钥,在任何场景都不应该流露出去

    服务端验证签名

    采用对应签名算法的验签方法进行验签

    客户端验证签名

    如果是采用非对称签名, 那么在客户端也可以验签,证明该token是经过认证的服务端签发的。

    JWT潜在问题及其解决方案

    • secret泄漏风险
        1. secret的保存应该和服务器证书的安全等级一致
        1. 保证每个用户一个secret,不要使用全局唯一secret
        1. 每个用户的secret和密码绑定, 在用户修改密码之后更新secret,并签发新的token
    • token泄漏。在客户端或传输过程中被窃取token, 通过HTTPS解决。
    • 重放攻击。http请求被拦截重放,这个安全性对于cookie和jwt是一样的。对于严格要求拒绝重放(当前会话被重放)的场景,可以每次生成token的时候,jwt的jti使用全局唯一ID,例如使用UUID,然后在redis中存储该ID,过期时间设置为token的过期时间,每次验证token有效性时,从redis里面取值判断,若存在,则说明是重放,拒绝该请求。该token在有效时间内只能使用一次
    • 续签问题/用户注销/修改密码。结合redis缓存维护token状态。当用户修改密码和注销时直接将redis中该用户的Token设置失效。
     
    软件开发
  2. 认证与授权
  3. 网络安全
  4. 【Linux常用命令】查找最大文件【MySQL】操作基础
    目录