一、什么是JWT认证
Json web token (JWT),根据官网的定义,是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准((RFC 7519)。该token被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场景。JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密。
二、JWT认证的特点
优点:
1.体积小,因而传输速度快
2.传输方式多样,可以通过URL/POST参数/HTTP头部等方式传输
3.严格的结构化。它自身(在 payload 中)就包含了所有与用户相关的验证消息,如用户可访问路由、访问有效期等信息,服务器无需再去连接数据库验证信息的有效性,并且 payload 支持为你的应用而定制化。
4.支持跨域验证,可以应用于单点登录
缺点:
1.安全性 由于jwt的payload是使用base64编码的,并没有加密,因此jwt中不能存储敏感数据。而session的信息是存在服务端的,相对来说更安全。
2.性能 jwt太长。由于是无状态使用JWT,所有的数据都被放到JWT里,如果还要进行一些数据交换,那载荷会更大,经过编码之后导致jwt非常长,cookie的限制大小一般是4k,cookie很可能放不下,所以jwt一般放在local storage里面。并且用户在系统中的每一次http请求都会把jwt携带在Header里面,http请求的Header可能比Body还要大。而sessionId只是很短的一个字符串,因此使用jwt的http请求比使用session的开销大得多。
3.一次性 无状态是jwt的特点,但也导致了这个问题,jwt是一次性的。想修改里面的内容,就必须签发一个新的jwt。
三、JWT的组成
JWT由三部分组成:header(头部)、payload(载荷)、signature(签名) JWT的格式为:header.payload.signature
加密后的样例展示:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJwYXNzd29yZCI6ImMyMGFkNGQ3NmZlOTc3NTlhYTI3YTBjOTliZmY2NzEwIiwiaWQiOjksImV4cCI6MTYzODM3Mjk5NSwiaWF0IjoxNjM3NzY4MTk1LCJ1c2VybmFtZSI6IjEyIn0.tslCAlrbk2m4YTiHhxW5exFe0JbwWaDSWG0xI8F4Vmg
接下来详细介绍三者的组成: 1.header(头部) JWT头部一般包含两部分信息: 1)typ(类别) 2)alg(加密算法,常用的是HMAC256、SHA256) 示例:
{
"alg": "HMAC256",
"typ": "JWT"
}
之后将头部的内容进行base64加密(这种加密是能够对称解密的),加密之后就构成了第一部分:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9。
2.payload(载荷) JWT的载荷部分是存放有效信息的地方,对于安全性比较高的程序,一般建议不要存放敏感信息,一般包含三部分信息: 1)需要添加进去的有效信息 2)token的签发时间和过期时间 3)私有的声明 示例:
{
"username": username,
"password": password,
"id": id,
"iat": iatDate, // 签发时间
"exp": expiresDate // 过期时间
...
}
同样将上述json对象进行base64加密后可以得到第二部分token字符串:eyJwYXNzd29yZCI6ImMyMGFkNGQ3NmZlOTc3NTlhYTI3YTBjOTliZmY2NzEwIiwiaWQiOjksImV4cCI6MTYzODM3Mjk5NSwiaWF0IjoxNjM3NzY4MTk1LCJ1c2VybmFtZSI6IjEyIn0
3.signature(签名) 这一部分一般设置一个公用密钥进行加密,只能在服务端解密。
四、JWT代码展示
JwtToken工具类
package com.kkkoke.networkrepair.util.token;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.kkkoke.networkrepair.util.MD5Util;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
public class JwtToken {
public static String SECRET = "kkkoke2021";
public static String creatToken(String username, String password, Long id) throws Exception {
Date iatDate = new Date();
Calendar nowTime = Calendar.getInstance();
nowTime.add(Calendar.DAY_OF_WEEK, 7);
Date expiresDate = nowTime.getTime();
Map<String, Object> map = new HashMap<>();
map.put("alg", "HMAC256");
map.put("typ", "JWT");
String token = JWT.create()
.withHeader(map)
.withClaim("username", username)
.withClaim("password", password)
.withClaim("id", id)
.withExpiresAt(expiresDate)
.withIssuedAt(iatDate)
.sign(Algorithm.HMAC256(SECRET));
return token;
}
public static Map<String, Claim> verifyToken(String token) throws Exception {
JWTVerifier verifier = JWT.require(Algorithm.HMAC256(SECRET)).build();
DecodedJWT jwt = null;
try {
jwt = verifier.verify(token);
} catch (Exception e) {
throw new RuntimeException("登录凭证已过去,请重新登录");
}
return jwt.getClaims();
}
}
解析示例
Map<String, Claim> jwt = JwtToken.verifyToken(token);
String username = jwt.get("username").asString();
String password = jwt.get("password").asString();
|