jwt是什么
jwt结构
jwt工具类 生成token和解析校验token
public class jwtUtil {
//秘钥加密解密都需要用到,所以定义成静态常量
public static final String SIGNATURE = "miyao";
/*测试生成jwt的token令牌三部分组成
. header(加密算法,类型)
. payload(负载,可以不重要可以用得到的用户信息)
. signature(签名,前两部跟base64加密和自己的秘钥)*/
/**
* 生成token
*
* @param map payload中需要放置的相关非敏感信息
* @return 返回的生成的token信息
*/
public static String getToken(Map<String, String> map) {
//设置一个时间,作为令牌的过期时间 ,设置过期时间为7天
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.DATE, 7);
//创建jwt builder
JWTCreator.Builder builder = JWT.create();
//生成jwt的 [header payload signature] 三部分数据
//遍历map集合,将传递过来的需要放在jwt中的信息放到payload中
map.forEach((k, v) -> {
builder.withClaim(k, v); //payload信息
});
//header信息可以省略,因为默认已经有算法和类型了,也可以在headerMap中设置算法
HashMap<String, Object> headerMap = new HashMap<>();
String token = builder.withHeader(headerMap) //header信息
.withExpiresAt(calendar.getTime()) //token过期时间
.sign(Algorithm.HMAC256(SIGNATURE));//签名
return token;
}
/**
* 验证token DecodedJWT 为解密之后的对象 可以获取payload中添加的数据
*/
public static DecodedJWT verifyToken(String token) {
//进行token的校验,注意使用同样的算法和同样的秘钥
return JWT.require(Algorithm.HMAC256(SIGNATURE)).build().verify(token);
}
}
设置拦截器,在方法执行前调用工具类中的token解析方法,进行token校验
/**
* 添加拦截器,拦截请求,校验token
*/
public class JWTInterceptor 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"); //从request中获取到请求头中的token,进行解析校验
try {
jwtUtil.verifyToken(token);//调用token解析的工具类进行解析,没有异常说明获取成功
return true; //请求放行
} catch (SignatureVerificationException e) {
e.printStackTrace();
map.put("msg", "签名不一致异常");
} catch (TokenExpiredException e) {
e.printStackTrace();
map.put("msg", "令牌过期异常");
} catch (AlgorithmMismatchException e) {
e.printStackTrace();
map.put("msg", "算法不匹配异常");
} catch (InvalidClaimException e) {
e.printStackTrace();
map.put("msg", "失效的payload异常");
} catch (Exception e) {
e.printStackTrace();
map.put("msg", "token无效");
}
//map异常的数据要返回给客户端需要转换成json格式 @ResponseBody 内置了jackson
String resultJson = new ObjectMapper().writeValueAsString(map);
response.setContentType("application/json;charset=utf-8");
response.getWriter().print(resultJson);
return false; //异常不放行
}
}
设置拦截器的拦截规则
@Configuration
public class interceptorConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new JWTInterceptor())
.addPathPatterns("/interceptorVerify/**") //拦截所有的路径
.excludePathPatterns("/login/**"); //放行login目录下的,因为生成token拦截就无法生成了
}
}
controller代码
@RestController
public class JWTTokenApplication {
/**
* 用户登录的接口,校验数据库之后,调用token工具类生成token返回客户端
*
* @param user 用户信息
* @return 返回的信息
*/
@Autowired
private UserService userService;
@PostMapping("/login")
public Map<String, String> login(@RequestBody User user) {
User userDb = userService.login(user);
if (userDb == null) {
throw new RuntimeException("用户不存在");
}
//用户校验成功
HashMap<String, String> map = new HashMap<>();
map.put("userName", userDb.getName());
String token = jwtUtil.getToken(map);
HashMap<String, String> resultMap = new HashMap<>();
resultMap.put("state", "认证成功");
resultMap.put("token", token);
return resultMap;
}
/**
* 单独对token进行解析校验测试,方式为post,token为参数传递,所以token要放在请求体中
* 请求时候带着token,对token进行解析,正确返回成功信息,不成功,抛出对应的异常
* @param token token令牌信息
* @return 验证的信息
*/
@PostMapping("/verify")
public Map<String,Object> verify(String token) {
HashMap<String, Object> map = new HashMap<>();
try {
//调用token解析的工具类进行解析,没有异常说明校验成功
DecodedJWT verifyToken = jwtUtil.verifyToken(token);
//解析成功,返回成功的信息
map.put("state", true);
map.put("msg", "认证成功!");
return map;
} catch (SignatureVerificationException e) {
e.printStackTrace();
map.put("msp", "签名不一致异常");
} catch (TokenExpiredException e) {
e.printStackTrace();
map.put("msg", "令牌过期异常");
} catch (AlgorithmMismatchException e) {
e.printStackTrace();
map.put("msg", "算法不匹配异常");
} catch (InvalidClaimException e) {
e.printStackTrace();
map.put("msg", "失效的payload异常");
} catch (Exception e) {
e.printStackTrace();
map.put("msg", "token无效");
}
map.put("state", false);
return map;
}
/**
* 拦截器拦截校验token 校验成功然后放行执行业务代码
* @param user
* @return
*/
@GetMapping("/interceptorVerify")
public String jwtInterceptorVerify(@RequestBody User user) {
//拦截器会拦截所有请求,此处进行正常的业务逻辑即可
//查询用户名是否存在
User userDb = userService.login(user);
if (userDb == null) {
return "用户不存在";
}
return "用户存在";
}
}
测试类中对jwt的使用测试
public class JwtTokenTest {
/**
* 令牌的获取
* 测试生成jwt的token令牌三部分组成
* header(加密算法,类型) . payload(负载,可以不重要可以用得到的用户信息) . signature(签名,前两部跟base64加密和自己的秘钥)
*/
@Test
public void jwtToken() {
HashMap<String, Object> map = new HashMap<>();//header中需要传递的map,无内容时使用jwt默认值
//设置一个时间,作为令牌的过期时间 ,设置过期时间为60秒
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.SECOND, 60);
String token = JWT.create().withHeader(map)//header中已经有默认的算法和类型
.withClaim("userId", 123456) //payload负载 工具类中通过map传递进行设置
.withClaim("userName", "zhangsan") //payload可以传递多个
.withExpiresAt(calendar.getTime()) //令牌不是无限期的,设置令牌的过期时间
.sign(Algorithm.HMAC256("miyao"));//签名设计加密算法,还有设置自己的秘钥
System.out.println(token);
}
/**
* 解析token,注意有效期内
*/
@Test
public void verifyTest() {
JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256("miyao")).build();
//进行token解析校验,参数为上方产生的token,注意有效期内
DecodedJWT verify = jwtVerifier.verify("eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyTmFtZSI6InpoYW5nc2FuIiwiZXhwIjoxNjIyMTIwNzgzLCJ1c2VySWQiOjEyMzQ1Nn0.wCobBUz5oRVmeaTBT5Omo_Ab3iTid8JrXRfqmxC-wqs");
//解析完成之后可以获取payload负载中自包含放置的信息
System.out.println(verify.getClaims());
System.out.println(verify.getClaim("userId").asInt());
System.out.println(verify.getClaim("userName").asString());
//获取过期时间
System.out.println(verify.getExpiresAt());
}
}