背景介绍
JSON Web Token (JWT) 是一个开放标准 ( RFC 7519 ),它定义了一种紧凑且自包含的方式,用于在各方之间安全地传输信息作为 JSON 对象。此信息可以验证和信任,因为它是数字签名的。JWT 可以使用密钥(使用HMAC算法)或使用RSA或ECDSA的公钥/私钥对进行签名。
虽然 JWT 可以加密以在各方之间提供保密性,但专注于签名令牌。签名的令牌可以验证其中包含的声明的完整性,而加密的令牌会向其他方隐藏这些声明。当使用公钥/私钥对令牌进行签名时,签名还证明只有持有私钥的一方才是签署它的一方。
总结:JSON Web token简称JWT, 是用于对应用程序上的用户进行身份验证的标记。也就是说, 使用 JWTS 的应用程序不再需要保存有关其用户的 cookie 或其他session数据。此特性便于可伸缩性, 同时保证应用程序的安全;
本质:JWT本质就是一个字符串,是一个经过加密算法加密和校验后的字符串。
官网截图:
应用场景:
-
授权:使用 JWT 最常见的场景。主要用于用户登录后,每个后续请求都将包含 JWT,从而允许用户访问该令牌允许的服务和资源等。
-
信息交换:JSON Web 令牌是进行安全传输信息的好方法。因为可以对 JWT 进行签名(例如,使用公钥/私钥对),所以您可以确定发件人就是他们所说的那个人。此外,由于使用标头和有效负载计算签名,还可以进行验证内容没有被篡改。
流程图
解析:
1、客户端初次登陆时,发送登录请求带客户客户端,登陆成功后创建JWTtoken,再将生成的token 响应给用户,token记录到客户端本地;
2、客户端访问其他接口时,发送受保护的API请求,请求authorzationHeader中添加token信息;
3、通过token获取信息,校验token的合法性,检查是否有被篡改过;
4、经过校验无误后,返回响应;
主要构成部分:
JWT是以( . )进行分隔的三部分组成
- 标头(头部信息)——主要是令牌的类型和所要使用的加密算法
- 有效载荷(数据)——用于用户数据的设置,采用JSON格式进行存储
- 自定义签名——用于自定义私钥,从而保证数据的安全高效
示例:
xxxxx . yyyyy . zzzzz
JWT 由标头、有效载荷、自定义签名 三者构成一个整体
1、标头:令牌类型+所要使用的加密算法
{
"alg": "HS256",
"typ": "JWT"
}
图例:
然后,这个 JSON 被Base64Url编码以形成 JWT 的第一部分。
2、有效载荷(数据):主要是存储的有效数据
{
"userID": "1234567890",
"name": "zhangsan",
"phone": "13312345678"
}
然后对有效负载进行Base64Url编码以形成 JSON Web 令牌的第二部分。
图例:
请注意! ! ! 对于已签名的令牌,此信息虽然受到保护以防篡改,但任何人都可以读取。除非已加密,否则请勿将机密信息放入 JWT 的有效负载或标头元素中。
3、自定义签名:自定义的签名私钥,是前两个(标头(头部信息)、有效载荷(承载数据))加密而成,进行校验,用于进一步保障数据的安全。
例如,如果想使用 HMAC SHA256 算法,签名将通过以下方式创建:
示例 生成的JWT加密数据
案例举证
1、pom文件中导入相关依赖
<!--jwt-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
2、编写测试案例
2-1生成token 进行数据加密
@Test
public void testtoken(){
//准备数据
HashMap map = new HashMap();
map.put("id",1001);//存入数据
map.put("name","zhangsan");//存入数据
map.put("phone","13312345678"); //存入数据
//使用JWT工具生成token
long now = System.currentTimeMillis();//获取当前系统时间
// LocalDate now1 = LocalDate.now();//获取当前系统时间
String token = Jwts.builder()
.signWith(SignatureAlgorithm.HS256, "zhangsan666") //指定加密算法和自定义私钥
.setClaims(map) //放入数据
.setExpiration(new Date(now + 30000)) //设置失效时间 30000ms
.compact();
System.out.println(token); //输出加密后的JWT结果
}
结果图例:
2-2解析Token
//解析token
/**
* SignatureException : token不合法
* ExpiredJwtException:token已过期
*/
@Test
public void testParseToken() {
//加密后的JWT数据
String token = "eyJhbGciOiJIUzI1NiJ9.eyJwaG9uZSI6IjEzMzEyMzQ1Njc4IiwibmFtZSI6InpoYW5nc2FuIiwiaWQiOjEwMDEsImV4cCI6MTY1ODkxNjk3Mn0.CnVGDRVhUpR9VkVK7eLpDLSHhFEw9Ad0ww_CcPV15Ak";
try {
Claims claims = Jwts.parser() //进行解析
.setSigningKey("zhangsan666") //自定义私钥
.parseClaimsJws(token)
.getBody(); //数据部分
Object id = claims.get("id"); //获取数据部分的主键
Object name = claims.get("name");
Object phone = claims.get("phone");
System.out.println(id + "--" +name+"--"+"--"+phone); //输出结果
}catch (ExpiredJwtException e) {
System.out.println("token已过期"); //超出失效时间,则抛出异常,输出token已过期
}catch (SignatureException e) {
System.out.println("token不合法"); //如果jwt加密数据被手动篡改,则抛出异常,输出token不合法
}
}
解码图例: