JWT简单使用
目录
一、概述
1.JWT是什么
JWT全称是JSON Web Token,通过JSON形式作为Web应用中的令牌,用于在各方之间安全地将信息作为JSON对象传输,在数据传输过程中还可以完成数据加密、签名等相关处理。
2.JWT能做什么?
授权
一旦用户登录,每个后续请求将包括JWT,从而允许用户访问该令牌的路由、服务和资源,单点登录是如今广泛使用JWT的一项功能。
信息交换
JWT是在各方之间安全地传输信息的好办法,因为可以对JWT进行签名,所以可以确保发件人是目标对象。
3.为什么要使用JWT?
在之前的Java Web项目中我们都使用session进行用户登录验证,而session是存在服务器中的,当用户量大的时候会使效率降低,并且在分布式开发中session需要共享。而JWT当用户登录成功之后,token就会存在客户端中,每次访问接口都会在"Header"中带上token,后端统一验证。
二、JWT结构
JWT生成的token令牌是由三部分组成:Header(表头)、payLoad(有效负载)、signature(签名),因此token通常为:xxxxx.yyyyy.zzzzz,而后端判就是用signature来判断是否为正确的用户和用户是否过期。
1.Header
表头通常由两部分组成:令牌的类型和所使用的签名算法,例如HMAC、SHA256或RSA。它会使用Base64编码组成JWT结构的第一部分。
【注意】:Base64是一种编码格式,是可以被反编译的。
//alg是所使用的签名算法,typ是JWT类型
{
"alg": "HS256"
} "typ": "JWT"
2.payLoad
令牌的第二部分是有效负载,其中包含声明。声明是有关实体(用户)和其他数据的声明。同样,它也使用Base64编码的。
//payLoad中存储的是用户的信息
{
"sub": "123456"
"name": "zhangsan"
}
3.signature
前面两部分都是用Base64进行编码的,即前端可以解开里面的信息,Signature需要编码后的header和payLoad以及我们提供的密匙,然后使用header中指定的算法进行签名,签名的作用是保证JWT没有被篡改过。
三、使用JWT
1.JWT代码
引入依赖
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.4.0</version>
</dependency>
生成token
//s为密匙也叫加密盐
String s="!abc@ert";
Calendar instance=Calendar.getInstance();
//设置过期时间,单位为秒,
instance.add(Calendar.SECOND,90);
//生成令牌
Map map=new HashMap();
String token= JWT.create()
// .withHeader(map)//添加header中的信息,是默认的,不加也可以
.withClaim("username","zhangsan")//添加payLoad中的用户信息
.withExpiresAt(instance.getTime())
.sign(Algorithm.HMAC256(s));//设置签名,保密
System.out.println(token);
生成结果
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.
eyJleHAiOjE2MzYxMjAxOTgsInVzZXJuYW1lIjoiemhhbmdzYW4ifQ.
OykEg2oSwpG4Qpg6J43UG8l9tHbgq4FFMF-PyUXtKtc
根据令牌和签名解析数据
//需要将加密盐secret和签名算法(HMAC256)带上
JWTVerifier jwtVerifier=JWT.require(Algorithm.HMAC256(secret)).build();
//解析token
DecodedJWT verify = jwtVerifier.verify("eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE2MzYxMjAxOTgsInVzZXJuYW1lIjoiemhhbmdzYW4ifQ.OykEg2oSwpG4Qpg6J43UG8l9tHbgq4FFMF-PyUXtKtc");
//获取用户名
/**
* 注意:
* 1.如果用户信息当时存放的是int类型,需要用asInt输出,如下:
* verify.getClaim("usernameId").asInt();
* 2.如果用户信息当时存放的是String类型,需要用asString输出,如下:
* verify.getClaim("usernameId").asString();
*/
verify.getClaim("username");
//获取过期时间
verify.getExpiresAt();
2.JWT的工具类封装
public class JWTUtil {
//加密盐/密匙
private final static String secret="!abcdrl";
//生成token,map中存入要放入payLoad中的用户信息
public static String getToken(Map<String,String> map){
Calendar instance=Calendar.getInstance();
//设置过期类型和时间,一般选天
instance.add(Calendar.DATE,5);
//存入header、payload和signature生成token
JWTCreator.Builder builder= JWT.create();
//遍历map,将用户信息存入
map.forEach((k,v)->{
builder.withClaim(k,v);
});
String token = builder.withHeader(new HashMap<>())//可以不加,有默认值
.withExpiresAt(instance.getTime())//指定过期时间
.withClaim("username", "zhangsan")
.withClaim("id", 5)
.sign(Algorithm.HMAC256(secret));//传入密匙和加密标签的算法
return token;
}
/**
* 解析verify,可以直接拿到这个在service层进行处理获取用户信息
* @param token
* @return
*/
public DecodedJWT verify(String token){
JWTVerifier jwtVerifier=JWT.require(Algorithm.HMAC256(secret)).build();
DecodedJWT verify = jwtVerifier.verify(token);
return verify;
}
}
3.SpringBoot整合JWT
只在controller层作演示
/**
* 用户登录,登录成功获取token返回给前端
* @param username
* @param password
* @return
*/
@RequestMapping("/user")
public String login(String username,String password){
//假设数据库中的用户名和密码如下:
String username_date="admin";
String password_date="admin";
if (username_date.equals(username) && password_date.equals(password)){
Map<String,String> map=new HashMap();
map.put("username",username_date);
map.put("password",password_date);
//获取token
String token = JWTUtil.getToken(map);
return token;
}
return null;
}
/**
* 客户端访问接口都需要带一个token,检查成功之后返回数据
* @param token
* @return
*/
@RequestMapping("getInfo")
public Map getInfo(String token){
System.out.println(token);
JWTUtil jwtUtil=new JWTUtil();
DecodedJWT verify = jwtUtil.verify(token);
if (verify!=null){
String username = verify.getClaim("username").asString();
String password = verify.getClaim("password").asString();
Map map=new HashMap();
map.put("username",username);
map.put("password",password);
return map;
}
return null;
}
拦截器中整合
使用拦截器之后就不用在每个代码中都验证token是否正确。
拦截器
public class JWTHandler implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//获取请求头中的令牌
String token = request.getHeader("token");
Map<String,Object> map=new HashMap<>();
try{
JWTUtil.verify(token);
}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){
map.put("msg","token失效");
}
map.put("state",false);//设置状态
String json = new ObjectMapper().writeValueAsString(map);
response.setContentType("application/json;charset=utf-8");
return false;
}
}
拦截器配置
@Configuration
public class JWTConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new JWTHandler())
.addPathPatterns("/**") //拦截所有路径
.excludePathPatterns("/login");//排除登录接口
}
}