目录:
(1)常见认证机制
(2)什么是JWT
(3)JWT快速Demo
(4)解析token
(5)token过期时间
(6)自定义负载声明
(1)常见认证机制
浏览器访问服务器的时候,会先去认证,认证的时候回传过来用户名和密码,让服务器进行相应的认证,认证通过会在创建session,同时在浏览器段创建Cookie对象,而且会把session放到Cookie里面,那下次我们客户端在进行相应访问的时候,客户端就会带上Cookie对象,服务器会受到Cookie对象,Cookie里面有session对象,拿session对象和服务器本身的session对象进行匹配实现转态的管理,如果是一至呢就返回相应的信息 ,关闭浏览器的时候呢Cookie会被删除,但是我们可以修改Cookie的失效时间使Cookie在一定时间内有效
5.1.4:
浏览器请求服务器的授权,带上用户名和密码请求登录,服务端受到请求会去验证请求和 密码,验证通过就会返回一个令牌,这个令牌会发给客户端,直接返回token,客户端受收到token以后,会把它存储起来,也是放在Cookie里面的,客户端每当向服务端请求资源的时候呢,就会带上服务端签发的token,服务端收到请求之后呢,就会验证客户端请求里面呢是否带着token,如果验证成功就会返回相应的数据,相对于第一种的验证更安全,相比第二种,第二种会创建session对象,令牌对应的字符串,字符创会比字符串资源更少,会比Cookie的认证方式节省更多的认证资源
(2)什么是JWT
(3)JWT快速Demo
测试类:JjwtdemoApplicationTests
package com.xxxx.jjwtdemo;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.impl.Base64Codec;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.Date;
@SpringBootTest
class JjwtdemoApplicationTests {
@Test
public void testCreateToken() {
//创建JwtBuilder对象
JwtBuilder jwtBuilder= Jwts.builder()
//声明的标识{"jti":"8888}
.setId("8888")
//主体,用户{"sub":"Rose"}
.setSubject("Rose")
//创建日期 {"ita":"xxxx"}
.setIssuedAt(new Date())
//使用算法
.signWith(SignatureAlgorithm.HS256,"xxxx");
//获取Jwt的token
String token=jwtBuilder.compact();
System.out.println(token);
System.out.println("=====================");
String[] split = token.split("\\.");
//使用BASE64进行解密
System.out.println(Base64Codec.BASE64.decodeToString(split[0]));
System.out.println(Base64Codec.BASE64.decodeToString(split[1]));
//签名是不能解码的涉及到盐,盐解密出来乱码
System.out.println(Base64Codec.BASE64.decodeToString(split[2]));
}
}
运行:
token:第一部分是头部信息,第二本分是荷载,第三部分是签名
再一次运行:
发现第一部分都是一样的,第二部分加了签发的时间,每次签发的token是不一样的,在没有盐的情况下是根本没有办法解密的
(4)解析token
上面我们已经创建了token,在web应用中呢,这种操作一般是有服务端进行,然后发送给客户端,客户端下次再向服务端发送请求的时候呢,就会携带token,就想电影院看电影,我们创建token的过程中就相当于把票token给客户,那客户进场看电影之前,还需要把票携带过来,我们去验证一下票是不是真的,服务端接收到token,就要解析除token的信息,比如说用户的ID、用户名称等等,根据这些信息我们再去数据库查询,返回响应的结果
下面测试一下来解析token:
package com.xxxx.jjwtdemo;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.impl.Base64Codec;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.Date;
@SpringBootTest
class JjwtdemoApplicationTests {
//解析token
@Test
public void testParseToken(){
String token="eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiI4ODg4Iiwic3ViIjoiUm9zZSIsImlhdCI6MTY1OTU2ODQzN30.WkjzR3_mu7nVSZOFlJFz7potgHPB7-vwoko1sSTcYZ8";
//解析token 获取负载中声明的对象
Claims claims=Jwts.parser()
.setSigningKey("xxxx")
.parseClaimsJws(token)
.getBody();
System.out.println("id:"+claims.getId());
System.out.println("subject:"+claims.getSubject());//主体
System.out.println("issueAt:"+claims.getIssuedAt());//签发日期
}
}
(5)token过期时间
我们签发的token是永久生效的,那么token必须有一个失效时间,就想电影票一样,为什么要添加失效时间呢,因为从服务器 发送的token服务器并不会自己做记录,所以说有一个弊端服务器端无法控制某一个token是否立刻失效,只能通过一个过期时间,来判断下一,到达失效时间进行失效的策略
package com.xxxx.jjwtdemo;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.impl.Base64Codec;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import java.text.SimpleDateFormat;
import java.util.Date;
@SpringBootTest
class JjwtdemoApplicationTests {
//创建token 失效时间
@Test
public void testCreateTokenHasExp() {
//当前系统时间
long now = System.currentTimeMillis();
//过期时间
long exp=now+60*1000;
//创建JwtBuilder对象
JwtBuilder jwtBuilder= Jwts.builder()
//声明的标识{"jti":"8888}
.setId("8888")
//主体,用户{"sub":"Rose"}
.setSubject("Rose")
//创建日期 {"ita":"xxxx"}
.setIssuedAt(new Date())
//使用算法
.signWith(SignatureAlgorithm.HS256,"xxxx")
//失效时间
.setExpiration(new Date(exp));
//获取Jwt的令牌token
String token=jwtBuilder.compact();
System.out.println(token);
System.out.println("=====================");
String[] split = token.split("\\.");
//使用BASE64进行解密把JWT的三部分进行分隔在解密
System.out.println(Base64Codec.BASE64.decodeToString(split[0]));
System.out.println(Base64Codec.BASE64.decodeToString(split[1]));
//签名是不能解码的涉及到盐,盐解密出来乱码
System.out.println(Base64Codec.BASE64.decodeToString(split[2]));
}
//解析token 失效时间
@Test
public void testParseTokenHasExp(){
String token="eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiI4ODg4Iiwic3ViIjoiUm9zZSIsImlhdCI6MTY1OTU3MjkyNCwiZXhwIjoxNjU5NTcyOTg0fQ.OP22h4k0fSirMlWUYMOGHkfrcgU3sMXOv13Tb2uj3C0";
//解析token 获取负载中声明的对象
Claims claims=Jwts.parser()
.setSigningKey("xxxx")
.parseClaimsJws(token)
.getBody();
System.out.println("id:"+claims.getId());
System.out.println("subject:"+claims.getSubject());//主体
System.out.println("issueAt:"+claims.getIssuedAt());//签发日期
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println("签发时间:"+simpleDateFormat.format(claims.getIssuedAt()));
System.out.println("过期时间:"+simpleDateFormat.format(claims.getExpiration()));
System.out.println("当前时间:"+simpleDateFormat.format(new Date()));
}
}
在1分钟之内运行:
超过失效时间1分钟之内运行解析token:报异常
(6)自定义负载声明
package com.xxxx.jjwtdemo;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.impl.Base64Codec;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import java.text.SimpleDateFormat;
import java.util.Date;
@SpringBootTest
class JjwtdemoApplicationTests {
//创建token(自定义声明)
@Test
public void testCreateTokenClaims() {
//创建JwtBuilder对象
JwtBuilder jwtBuilder= Jwts.builder()
//声明的标识{"jti":"8888}
.setId("8888")
//主体,用户{"sub":"Rose"}
.setSubject("Rose")
//创建日期 {"ita":"xxxx"}
.setIssuedAt(new Date())
//使用算法
.signWith(SignatureAlgorithm.HS256,"xxxx")
//自定义声明信息
.claim("roles","admin")
.claim("logo","xxx.jpg");
//获取Jwt的令牌token
String token=jwtBuilder.compact();
System.out.println(token);
System.out.println("=====================");
String[] split = token.split("\\.");
//使用BASE64进行解密把JWT的三部分进行分隔在解密
System.out.println(Base64Codec.BASE64.decodeToString(split[0]));
System.out.println(Base64Codec.BASE64.decodeToString(split[1]));
//签名是不能解码的涉及到盐,盐解密出来乱码
System.out.println(Base64Codec.BASE64.decodeToString(split[2]));
}
//解析token(自定义声明)
@Test
public void testParseTokenByClaims(){
String token="eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiI4ODg4Iiwic3ViIjoiUm9zZSIsImlhdCI6MTY1OTU3NjY3Nywicm9sZXMiOiJhZG1pbiIsImxvZ28iOiJ4eHguanBnIn0.NtmcmdRQEZBlC8XLXDfsxP_0lJSklFdcYczfICluEO0";
//解析token 获取负载中声明的对象
Claims claims=Jwts.parser()
.setSigningKey("xxxx")
.parseClaimsJws(token)
.getBody();
System.out.println("id:"+claims.getId());
System.out.println("subject:"+claims.getSubject());//主体
System.out.println("issueAt:"+claims.getIssuedAt());//签发日期
//获取自定义声明
System.out.println("roles:"+claims.get("roles"));
System.out.println("logo:"+claims.get("logo"));
}
}
(7)扩展JWT内容存储
Jwt可以自定义声明,去扩展存储的内容,需要去继承一个实现类JWT的内容增强器