认识并使用JWT
- 一、互联网世界的用户认证
- 二、对JWT的基本认知
- 三、JWT的原理
- 1 Header
- 2 Payload
- 3 Signature
- 4 [参考资料](https://www.ruanyifeng.com/blog/2018/07/json_web_token-tutorial.html)
- 四、使用JWT
- 1、引入依赖
- 2、jwt的生成与解析
- 3、测试
- 3.1 生成jwt
- 3.2 解析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):签发人
(2)exp (expiration time):过期时间
(3)sub (subject):主题
(4)aud (audience):受众
(5)nbf (Not Before):生效时间
(6)iat (Issued At):签发时间
(7)jti (JWT ID):编号
除了官方字段,咱还可以在这个部分定义私有字段,如下所示:
{"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