JWT

概述

  • JWT 基于 token 的鉴权机制,基于 token 的鉴权机制类似于 http 协议也是无状态的,它不需要服务端保留用户认证信息或回话信息,这意味着基于 token 认证机制不需要考虑存在哪台服务器,为应用扩展提供了便利,它的运行流程:

    1. 用户使用用户密码来请求服务器
    2. 服务器进行验证用户信息
    3. 服务器通过验证发送用户一个token
    4. 客户端存储token,并在每次请求时在HTTP头附送上这个token值
    5. 服务端验证token值,并返回数据
  • 通常把JWT Base64编码后保存在HTTP请求头里,这个 token 在每次请求时传给服务端,另外,服务端要支持CORS(跨域访问):Access-Control-Allow-Origin:*

构成

  • 第一部分称它为头部(header),第二部分为载荷(payload),第三部分为签证(signature),将这三部分连接成一个完整字符串就构成了 JWT
    • header
      • jwt 头部承载两部分信息,声明类型,此处为 jwt ,声明加密算法,通常为 HMAC SHA256,完整头部 JSON 如:{'typ':'JWT','alg':'HS256'}
      • 然后进行 base64 加密(可以对称解密),构成第一部分:eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
    • payload
      • 载荷是存放有效信息的地方,包括三部分:标签中注册的声明、公共的声明、私有的声明
      • 标准中的注册的声明如:iss(jwt签发者)、sub(jwt所面向用户)、aud(接收jwt的一方)、exp(jwt过期时间)、nbf(定义jwt生效时间)、iat(jwt签发时间)、jti(jwt唯一身份标识,作为一次性token,从而回避重放攻击)
      • 公共的声明:公共的声明可以添加任何信息,一般为用户相关信息或业务信息,但不要加敏感信息,该部分在客户端可解密
      • 私有的声明:私有声明是提供者和消费者所共同定义的声明,不建议存放敏感信息,因为是base64对称加密,该部分信息可以归类为铭文信息
      • 定义一个payload:{"sub":"1234567890","name":"John Doe","admin":true}
      • base64加密得到 JWT 第二部分:eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9
    • signature
      • 该部分是一个签证信息,由三部分组成:header(base64后)、payload(base64后)、secret(自定义密钥)
      • 由加密后的 header 和 payload 连接组成的字符串,然后通过 header 中声明的加密方式进行 secret 组合加密,构成 JWT 第三部分
    • 注意:secret 是保存在服务端的,JWT 签发生成也是在服务端,secret 就是用来进行 JWT 签发和 JWT 验证的,所以,它是服务端的私钥,任何场景都不能泄漏出去,一旦客户端知道这个 secret 则意味客户端可以自我签发 JWT

总结

优点:
    ■ 因 JSON 通用性,所以 JWT 可跨语言支持
    ■ payload 可存储业务逻辑信息(非敏感性)
    ■ 便于传输,字节占用很小
    ■ 不需要存储在服务端,易于扩展
安全相关:
    ■ 不应该在 JWT 的 payload 存储敏感信息,该部分是可以解密的
    ■ 保护好 secret 私钥,该私钥非常重要
    ■ 如果可以采用 HTTPS 协议   

与 Session 区别

  • Session 通过 cookie 传输,存储在服务器,服务器通过 cookie 中的 sessionID 获取当前会话用户,对于单台服务器没问题,但多服务器就涉及到共享 Session ,而且认证用户多时 Session 会占用大量服务器内存
  • JWT 存储在客户端,服务器不需要存储 JWT ,JWT 里有用户 ID,服务器拿到 JWT 后验证可以获得用户信息,也就实现了 Session 功能,但是是无状态的,只要签名秘钥足够安全就能保证 JWT 可靠性

JWT 为什么能保证信息可靠性?

  • 比如现在有 token :

eyJhbGciOiJIUzI1NiJ9.eyJleHAiOjE0OTg0ODIxNTQsInN1YiI6InF1ZGluZyIsInVzZXJJZCI6IjEwMzc5NDAxIiwicm9sZSI6ImFkbWluIn0.-YFTYJ6FLlIQqD4G3hYcWvYlYE8H9eAA2369WEcJFVY

{
    "alg": "HS256"
}
{
    "exp": 1498482154,
    "sub": "linyuan",
    "userId": "123456",
    "role": "admin"
}
Sign ��6蒥!ୡaůbV��^땄pU
  • 假设 payload 被劫持了,其他人把 userId 修改为自己的,如 123456 ,但没有签名秘钥,所以没法生成签名,服务端收到该 Token 后,先用 Base64 解码出信息,然后重新生成 Signtrue,使用该 Signtrue 与客户端传来的 Signtrue 对比,一样则证明没被修改,否则拒绝请求

JJWT使用

  1. 通过Maven引入JJWT库:
   
        io.jsonwebtoken
        jjwt
        0.7.0

  1. 签发 JWT
public static String createJWT() {
        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
        SecretKey secretKey = generalKey();
    JwtBuilder builder = Jwts.builder()
            .setId(id)          // JWT_ID
            .setAudience("")    // 接受者
        .setClaims(null)   // 自定义属性
            .setSubject("")     // 主题
        .setIssuer("")     // 签发者
            .setIssuedAt(new Date())    // 签发时间
        .setNotBefore(new Date())  // 失效时间
            .setExpiration(long)      // 过期时间
        .signWith(signatureAlgorithm, secretKey);// 签名算法以及密匙
            return builder.compact();
}
  1. 验证 JWT
public static Claims parseJWT(String jwt) throws Exception {
    SecretKey secretKey = generalKey();
            return Jwts.parser()
            .setSigningKey(secretKey)
            .parseClaimsJws(jwt)
            .getBody();
}
  1. 当 JWT验证失败时会抛出异常,常见异常有:
    • SignatureException:签名错误异常
    • MalformedJwtException:JWT格式错误异常
    • ExpiredJwtException:JWT过期异常
    • UnsupportedJwtException:不支持的JWT异常

你可能感兴趣的:(JWT)