认识并使用JWT

一、互联网世界的用户认证

  • 以调用openai的api为例:
curl https://api.openai.com/v1/chat/completions   -H "Content-Type: application/json"   -H "Authorization: Bearer $OPENAI_API_KEY"   -d '{
    "model": "gpt-3.5-turbo",
    "messages": [
      {
        "role": "system",
        "content": "You are a poetic assistant, skilled in explaining complex programming concepts with creative flair."
      },
      {
        "role": "user",
        "content": "Compose a poem that explains the concept of recursion in programming."
      }
    ]
  }'
  • Authorization: Bearer $OPENAI_API_KEY,其中$OPENAI_API_KEY便是用于用户认证的token。
  • 那用户怎么获取这个token呢?
    一般,咱先通过用户名和密码登录网站,然后网站分配一个token。
  • 在Java中,有JWT技术来实现这种需求。

二、对JWT的基本认知

  • JWT:JSON Web Token

我理解:将json格式的数据转换为在Web中用于用户认证的token。

  • JWT是一种基于Token的用户认证技术。
  • 思路:当用户登录时,服务器会创建一个包含用户信息的JWT,并将其发送回用户。然后,用户可以使用该Token来进行后续的授权请求。在每次请求中,服务器都会验证JWT来确认用户的身份,然后才会处理请求。

三、JWT的原理

  • JWT由三部分组成:头部(Header)、载荷(Payload)和签名(Signature)。
    在这里插入图片描述

1 Header

  • Header 部分原本是一个 JSON 对象,用于描述 JWT 的元数据,如下所示:
{
  "alg": "HS256",
  "typ": "JWT"
}

(1)alg属性表示签名的算法,默认是 HMAC SHA256(写成 HS256);
(2)typ属性表示这个令牌(token)的类型,JWT统一写为"JWT"。

2 Payload

  • Payload 部分原本也是一个 JSON 对象,用来存放实际需要传递的数据。JWT 规定了7个官方字段,供选用。

(1) iss (issuer):签发人。这个字段用来指明这个JWT是由谁签发的。例如,你可能会在这个字段中包含你的公司或你的应用程序的名称。
(2)exp (expiration time):这个字段是一个时间戳,表示这个JWT何时过期。经过这个时间点后,JWT将无法被接受。
(3)sub (subject):主题。这个字段是JWT的主题,通常是用户的ID。它可以用来确定JWT是关于谁的。
(4)aud (audience):受众。这个字段用来指明这个JWT是给谁的。例如,这可能是一个特定的用户,或者一个包含多个用户的组。
(5)nbf (Not Before):生效时间。这个字段是一个时间戳,表示这个JWT何时开始有效。即使在这个时间点之前JWT被发送,它也不会被接受。
(6)iat (Issued At):签发时间。这个字段是一个时间戳,表示这个JWT何时被签发的。在某些情况下,你可能会想要检查这个时间,以确保JWT没有在很早以前就被签发。
(7)jti (JWT ID):编号。这个字段是JWT的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。每个JWT都有一个唯一的ID,这样就可以跟踪每个特定的JWT,以及它是何时和由谁使用的。

除了官方字段,咱还可以在这个部分定义私有字段,如下所示:

{
  "name": "Forrest",
  "admin": true
}

注意:不要把秘密信息放在这个JSON对象中,因为通常这个JSON对象被转换为字符串后,默认是不加密的,这就会被别人解析回JSON对象,里面的信息就暴露了。

3 Signature

  • Signature 部分是对前两部分的签名,防止数据篡改。
  • 首先,需要指定一个密钥(secret)。这个密钥只有服务器才知道,不能泄露给用户。然后,使用 Header 里面指定的签名算法(默认是 HMAC SHA256),按照下面的公式产生签名:
HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  secret)
  • 算出签名以后,把 Header、Payload、Signature 三个部分拼成一个字符串,每个部分之间用"点"(.)分隔,就可以返回给用户。

4 参考资料

四、使用JWT

1、引入依赖

<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.9.1</version>
</dependency>
  • 还必须引入:
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-core</artifactId>
    <version>2.11.3</version>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.11.3</version>
</dependency>

否则,会报错:
java.lang.NoClassDefFoundError: com/fasterxml/jackson/core/util/JacksonFeature
at com.fasterxml.jackson.databind.ObjectMapper.<init>(ObjectMapper.java:656)
at com.fasterxml.jackson.databind.ObjectMapper.<init>(ObjectMapper.java:558)

2、jwt的生成与解析

public class JwtUtils {
    private static final String secret = "IntelliJ IDEA";
    // 7天有效期
    public static final long EXPIRE_TIME = 1000 * 60 * 60 * 24 * 7;

    /**
     * 生成jwt
     */
    public static String encode(Map<String, Object> headerMap, Map<String, Object> playloadMap)  {
        return Jwts.builder()
                .setHeader(headerMap)
                .setClaims(playloadMap)
                .signWith(SignatureAlgorithm.HS256, secret)
                .compact();
    }

    /**
     * 解析jwt
     */
    public static Map<String, Object> decode(String jwt) {
        return Jwts.parser()
                .setSigningKey(secret)
                .parseClaimsJws(jwt)
                .getBody();
    }
}

3、测试

3.1 生成jwt

@Test
public void testEncode() {
    Map<String, Object> headerMap = new HashMap<>();
    headerMap.put("type", "JWT");
    headerMap.put("alg", "HS256");

    Map<String, Object> playloadMap = new HashMap<>();
    playloadMap.put("username", "Forrest");
    playloadMap.put("exp", System.currentTimeMillis() + JwtUtils.EXPIRE_TIME);
    playloadMap.put("jti", UUID.randomUUID().toString());

    for (Map.Entry<String, Object> entry : playloadMap.entrySet()) {
        System.out.println("Key = " + entry.getKey() + ", Value = " + entry.getValue());
    }

    String jwt = JwtUtils.encode(headerMap, playloadMap);
    System.out.println(jwt);
}

Key = exp, Value = 1706023265921
Key = jti, Value = c212fde2-abf4-4149-9c48-c55c1a029e79
Key = username, Value = Forrest
eyJ0eXBlIjoiSldUIiwiYWxnIjoiSFMyNTYifQ.eyJleHAiOjE3MDYwMjMyNjU5MjEsImp0aSI6ImMyMTJmZGUyLWFiZjQtNDE0OS05YzQ4LWM1NWMxYTAyOWU3OSIsInVzZXJuYW1lIjoiRm9ycmVzdCJ9.t-rnO5CgC5DuNShsWIHxu_HKAgIU75tDQnlDcIBCmm0

3.2 解析jwt

@Test
public void testDecode() {
    String jwt = "eyJ0eXBlIjoiSldUIiwiYWxnIjoiSFMyNTYifQ.eyJleHAiOjE3MDYwMjI3MzM2MTUsImp0aSI6ImFkMTRiZDEyLWNlN2EtNDBjNi1iYTJkLWU0MDllMTY4ZjIwYyIsInVzZXJuYW1lIjoiRm9ycmVzdCJ9.NuNW5PckkwWpFRobreKjrU6pDdosHnc3J2KhwxFW4xU";
    Map<String, Object> map = JwtUtils.decode(jwt);
    for (Map.Entry<String, Object> entry : map.entrySet()) {
        System.out.println("Key = " + entry.getKey() + ", Value = " + entry.getValue());
    }
}

Key = exp, Value = 1706023265921
Key = jti, Value = c212fde2-abf4-4149-9c48-c55c1a029e79
Key = username, Value = Forrest

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值