JWT学习教程

1. 简介

JSON Web Token(JWT)是一个开放标准(RFC 7519),它定义了一种紧凑和自包含的方式,用于在各方之间作为JSON对象安全地传输信息。作为标准,它没有提供技术实现,但是大部分的语言平台都有按照它规定的内容提供了自己的技术实现。

2. JWT的使用场景

什么时候应该使用 JWT 呢?

下列是使用 JSON Web Token 的主要场景:

  • 认证授权 (Authorization) :这是使用 JWT 的最常见场景。一旦用户登录,后续每个请求都将包含 JWT,允许用户访问该 Token 允许的路由、服务和资源。单点登录是现在广泛使用的 JWT 的一个特性,因为它的开销很小,而且可以轻松地跨域使用。
  • 信息交换 (Information Exchange) :对于安全的在各方之间传输信息而言,JSON Web Token 无疑是一种很好的方式。因为 JWT 可以被签名,例如,用公钥/私钥对,你可以确定发送人就是它们所说的那个人。另外,由于签名是使用头和有效负载计算的,您还可以验证内容没有被篡改。

3. JWT 优缺点

3.1 优点

跨语言:支持主流语言。
自包含:包含必要的所有信息,如用户信息和签名等。
易传递:很方便通过 HTTP 头部传递。
JWT 默认是不加密,但也是可以加密的。生成原始 Token 以后,可以用密钥再加密一次。JWT 不加密的情况下,不能将秘密数据写入 JWT。

JWT 不仅可以用于认证,也可以用于交换信息。有效使用 JWT,可以降低服务器查询数据库的次数。

3.2 缺点

由于服务器不保存 session 状态,因此 JWT 无法在使用过程中废止某个 token,或更改 token 的权限。一旦 JWT 签发,在到期之前就会始终有效,除非服务器部署额外的逻辑。
JWT 本身包含认证信息,一旦泄露,任何人都可以获得该令牌的所有权限。为了减少盗用,JWT 的有效期应该设置得比较短。对于一些比较重要的权限,使用时应该再次对用户进行认证。
为减少盗用,JWT 不应该使用 HTTP 协议明码传输,要使用 HTTPS 协议传输。

4. JWT 和 Session 对比

由于 HTTP 协议是无状态协议,所以 HTTP 协议本身是无法区别身份的。传统上,一般通过 Session 配合 Cookie 机制解决身份识别问题。但是在高并发、高流量的服务集群化时,Session 机制遇到极大挑战。而 JWT 认证机制就可以完美的解决这些问题。

4.4 JWT 总结

JWT 和 Session 相同点是,它们都是存储用户信息;然而,Session 是在服务器端的,而 JWT 是在客户端的。Session 方式存储用户信息的最大问题在于要占用大量服务器内存,增加服务器的开销。而 JWT 方式将用户状态分散到了客户端中,可以明显减轻服务端的内存压力。Session 的状态是存储在服务器端,客户端只有 session id;而 Token 的状态是存储在客户端。

5. JWT 组成结构

5. 1 JWT Header

头部包含两部分:声明类型和使用的哈希算法,通常直接使用HMAC SHA256,就是HS256。

{
    "typ": "JWT",
    "alg": "HS256"
}

将头部进行 Base64 编码构成第一部分。Base64 是一种用 64 个字符来表示任意二进制数据的方法,常用于在 URL、Cookie、网页中传输少量二进制数据。

5.2 JWT Payload

也称为JWT claims,放置需要传输的信息,有三类:

  • 保留claims,主要包括iss发行者、exp过期时间、sub主题、aud用户等。
  • 公共claims,定义新创的信息,比如用户信息和其他重要信息。
  • 私有claims,用于发布者和消费者都同意以私有的方式使用的信息。

JWT规定7个官方字段,供选用:

  1. iss (issuer):签发人。
  2. exp (expiration time):过期时间。
  3. nbf (Not Before):生效时间。
  4. aud (audience):受众。
  5. sub (subject):主题。
  6. iat (Issued At):签发时间。
  7. jti (JWT ID):编号。
    JwtBuilder中与官方字段对应的方法如下:
    在这里插入图片描述

支持定义私有字段,示例:

{
    "iss": "jwt.io",
    "exp": 1496199995458,
    "name": "sinwaj",
    "role": "admin"," 
}

JwtBuilder中对应的方法之一为:
在这里插入图片描述

JWT 默认是不加密的,任何人都可以读到,所以不要把秘密信息放在这个部分。

5.3 JWT Signature

生成签名部分,需要准备:编码的 header、编码的 payload 和一个秘钥,签名算法是 header 指定的算法,然对这些数据进行签名。

签名公式如下:

HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)

其中:secret 为加密使用的盐,也可以认为是私钥,千万不能泄露。

JwtBuilder中设置signature对应的方法如下:
在这里插入图片描述

5.4 完整的 JWT

将 header、payload 和 signature 三部分用 . 连接成一个完整的字符串,构成了最终的 JWT:

例如:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

其中:

  • eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9 是 Base64 编码的 header。
  • eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9 是 Base64 编码的 payload。
  • TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ 是对 header、payload 的签名。

注意:secret 保存在服务器端,JWT 的生成也在服务器端,secret 用来进行 jwt 的签发和 jwt 的验证,它就是服务端的私钥。

所以,在任何场景下都不应该泄露出去。一旦客户端得知这个 secret,那就意味着客户端是可以自我签发 jwt。

6.JWT工作流程

在这里插入图片描述

7. 工具类

public class JwtUtil {

    //秘钥
    private static final String secretKey = "kafsdhfkajsdlhdassfadsgkja";

    //加密算法
    private static final SignatureAlgorithm signAlg = SignatureAlgorithm.HS256;

    //过期时间
    private static final long expTime = 1000*60*60*24l;

    /**
     * 生成token
     * @param userId 用户Id
     * @param userName 用户名
     * @param roles 角色
     */
    public static String generateToken(long userId,String userName,String roles) {

        String token = Jwts
                .builder()
                //claim() 定义私有字段
                .claim("userId",userId)
                .claim("userName",userName)
                .claim("roles",roles)
                //token签发时间
                .setIssuedAt(new Date())
                //token过期时间
                .setExpiration(new Date(System.currentTimeMillis()+expTime))
                //signature部分
                .signWith(signAlg,secretKey)
                .compact();

        //业务逻辑

        //返回token
        return token;
    }

    /**
     * 解析token,获取保存的信息
     * @param token 需要解析的token
     */
    public static Claims getClaims(String token) {
        Claims claims = null;
        try {
            claims = Jwts.parser()
                    //用于验证token的秘钥,如果token没有signature部分,则该秘钥不会被使用
                    .setSigningKey(secretKey)
                    .parseClaimsJws(token)
                    .getBody();
        }catch (Exception e) {
            System.out.println("----------token解析失败------------");
        }

        return claims;
    }
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值