1. 什么是JWT
Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准((RFC 7519).该token被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场景。JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密。
通
俗
说
:
\color{#FF3030}{通俗说:}
通俗说:
- 在数据传输过程中还可以完成数据加密,签名等相关处理。
2.JWT应用场景
- 授 权 : \color{#FF3030}{授权:} 授权::一旦用户登陆,每个后续请求包括JWT,从而允许用户访问该令牌的路由和资源:单点登录
- 信 息 交 换 : \color{#FF3030}{信息交换:} 信息交换:信息交换 :在A,B系统之前信息传输。
JWT 原理
JWT组成
header.payload.signature用点隔开
- 头部(header)
{
"typ": "JWT",
"alg": "HS256"
}
Base64 编码
- 有效载荷(payload)
{
"uid": "1234567890",
"name": "John Doe",
"admin": true
}
Base64 编码
-
签名(signature)
-
该部分是签名,以token的前两部分作为明文,用共同协商好的秘钥进行签名。
-
如:用的第一部分选择的算法是HMAC-SHA256,执行以下操作得出签名值
HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)
使用JWT
1. 引 入 依 赖 : \color{#9B30FF}{1.引入依赖:} 1.引入依赖:
<!-- https://mvnrepository.com/artifact/com.auth0/java-jwt -->
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.10.3</version>
</dependency>
2. 生 成 t o k e n : \color{#9B30FF}{2.生成token:} 2.生成token:
//密钥
public static final String SECRET = "sdjhakdhajdklsl;o653632";
//过期时间:秒
public static final int EXPIRE = 5;
Calendar nowTime = Calendar.getInstance();
nowTime.add(Calendar.SECOND, EXPIRE);
Date expireDate = nowTime.getTime();
Map<String, Object> map = new HashMap<>();
map.put("alg", "HS256");
map.put("typ", "JWT");
String token = JWT.create()
.withHeader(map)//头
.withClaim("userId", userId)
.withClaim("userName", userName)
.withSubject("测试")//
.withIssuedAt(new Date())//签名时间
.withExpiresAt(expireDate)//过期时间
.sign(Algorithm.HMAC256(SECRET));//签名
验 证 : \color{#9B30FF}{验证:} 验证:
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();
}
JWT工具类
public class JWTutils {
/*
生成token:header.payload.single
*/
private static final String SING="!kjlk34";//签名
public static String getToken(Map<String,String> map){
Calendar nowTime = Calendar.getInstance();
nowTime.add(Calendar.DATE, 7);
Date expireDate = nowTime.getTime();
JWTCreator.Builder builder = JWT.create();
//payload
map.forEach((k,v)->{
builder.withClaim(k,v);
});
String token = builder.withExpiresAt(nowTime.getTime())
.sign(Algorithm.HMAC256(SING));
System.out.println(token);
return token;
}
/*
* 验证token合法性。
* */
public static void verify(String token){
JWTVerifier verifier = JWT.require(Algorithm.HMAC256(SING)).build();
DecodedJWT jwt = null;
try {
jwt = verifier.verify(token);
System.out.println(jwt.getClaims().get("userId").toString());
}catch (Exception e){
throw new RuntimeException("凭证已过期,请重新登录");
}
}
/*
* 获取token中信息
* */
public static DecodedJWT getTokenInfo(String token){
DecodedJWT TokenInfo = JWT.require(Algorithm.HMAC256(SING)).build().verify(token);
return TokenInfo;
}
}
Springboot 整合JWT
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/login")
public Map<String,Object> login(@RequestBody User user){
HashMap<String, Object> map = new HashMap<>();
User login = userService.login(user);
HashMap<String, String> map1 = new HashMap<>();
map1.put("id",login.getUid());
map1.put("name",login.getName());
String token = JWTutils.getToken(map1);
if(login!=null){
map.put("stutas",true);
map.put("token",token);
map.put("msg","登录成功");
}else{
map.put("stutas",false);
map.put("msg","登录失败");
}
return map;
}
假如有个受保护的接口,那么每次都需要传参(token)代码会冗余
2.
优
化
:
使
用
拦
截
器
−
如
果
是
S
p
r
i
n
g
C
l
o
u
d
就
可
以
使
用
网
关
\color{#9B30FF}{2.优化:使用拦截器-如果是SpringCloud就可以使用网关}
2.优化:使用拦截器−如果是SpringCloud就可以使用网关
添加拦截器
public class JWTIntercepter implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
HashMap<String, Object> map = new HashMap<>();
//获取请求头中令牌
String token = request.getHeader("token");
try {
JWTutils.verify(token);
return true;
}catch (SignatureVerificationException e){
e.printStackTrace();
map.put("msg","无效签名");
}catch (TokenExpiredException e){
e.printStackTrace();
map.put("msg","token过期");
}catch (AlgorithmMismatchException e){
e.printStackTrace();
map.put("msg","token算法不一致");
}catch (Exception e){
e.printStackTrace();
map.put("msg","token无效");
}
//将Map转换为json
String s = new ObjectMapper().writeValueAsString(map);
response.setContentType("application/json;charset=UTF-8");
response.getWriter().write(s);
return false;
}
配置拦截器
方式二:
pom
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
</dependency>
public class AppJwtUtil {
// TOKEN的有效期一天(S)
private static final int TOKEN_TIME_OUT = 3_600;
// 加密KEY
private static final String TOKEN_ENCRY_KEY = "MDk4ZjZiY2Q0NjIxZDM3M2NhZGU0ZTgzMjYyN2I0ZjY";
// 最小刷新间隔(S)
private static final int REFRESH_TIME = 300;
// 生产ID
public static String getToken(Long id){
Map<String, Object> claimMaps = new HashMap<>();
claimMaps.put("id",id);
long currentTime = System.currentTimeMillis();
return Jwts.builder()
.setId(UUID.randomUUID().toString())
.setIssuedAt(new Date(currentTime)) //签发时间
.setSubject("system") //说明
.setIssuer("heima") //签发者信息
.setAudience("app") //接收用户
.compressWith(CompressionCodecs.GZIP) //数据压缩方式
.signWith(SignatureAlgorithm.HS512, generalKey()) //加密方式
.setExpiration(new Date(currentTime + TOKEN_TIME_OUT * 1000)) //过期时间戳
.addClaims(claimMaps) //cla信息
.compact();
}
/**
* 获取token中的claims信息
*
* @param token
* @return
*/
private static Jws<Claims> getJws(String token) {
return Jwts.parser()
.setSigningKey(generalKey())
.parseClaimsJws(token);
}
/**
* 获取payload body信息
*
* @param token
* @return
*/
public static Claims getClaimsBody(String token) {
try {
return getJws(token).getBody();
}catch (ExpiredJwtException e){
return null;
}
}
/**
* 获取hearder body信息
*
* @param token
* @return
*/
public static JwsHeader getHeaderBody(String token) {
return getJws(token).getHeader();
}
/**
* 是否过期
*
* @param claims
* @return -1:有效,0:有效,1:过期,2:过期
*/
public static int verifyToken(Claims claims) {
if(claims==null){
return 1;
}
try {
claims.getExpiration()
.before(new Date());
// 需要自动刷新TOKEN
if((claims.getExpiration().getTime()-System.currentTimeMillis())>REFRESH_TIME*1000){
return -1;
}else {
return 0;
}
} catch (ExpiredJwtException ex) {
return 1;
}catch (Exception e){
return 2;
}
}
/**
* 由字符串生成加密key
*
* @return
*/
public static SecretKey generalKey() {
byte[] encodedKey = Base64.getEncoder().encode(TOKEN_ENCRY_KEY.getBytes());
SecretKey key = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");
return key;
}
public static void main(String[] args) {
/* Map map = new HashMap();
map.put("id","11");*/
System.out.println(AppJwtUtil.getToken(1102L));
Jws<Claims> jws = AppJwtUtil.getJws("eyJhbGciOiJIUzUxMiIsInppcCI6IkdaSVAifQ.H4sIAAAAAAAAADWLQQqEMAwA_5KzhURNt_qb1KZYQSi0wi6Lf9942NsMw3zh6AVW2DYmDGl2WabkZgreCaM6VXzhFBfJMcMARTqsxIG9Z888QLui3e3Tup5Pb81013KKmVzJTGo11nf9n8v4nMUaEY73DzTabjmDAAAA.4SuqQ42IGqCgBai6qd4RaVpVxTlZIWC826QA9kLvt9d-yVUw82gU47HDaSfOzgAcloZedYNNpUcd18Ne8vvjQA");
Claims claims = jws.getBody();
System.out.println(claims.get("id"));
}
}