用JWT生成token令牌,代替以前的session
以前我们完成登陆验证后,会将user信息放入session中
现在,在完成登陆验证后我们new一个map,然后将想存储的用户信息以key value的形式存储在map中(这个map也就是下面getToken方法中需要传入的参数),通过JWT.getToken(token)的到生成的token令牌
这样一来 我们之后就可以在发请求受保护的接口的时候在header中带上这个token(header中自动会有这个token) 然后用拦截器进行验证。
如果想获取token中的信息,可以在controller的参数中加上request,通过request.getHeader.get("token")拿到token... 具体流程见下面代码
maven依赖
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.10.3</version>
</dependency>
JWT工具类
/**
* 将JWT封装成工具类
*/
public class JWTUtils {
// 设置sign(密钥)
public static final String SIGN = "qweasd";
/**
* 生成token 形式为:header.payload.sign
*/
public static String getToken(Map<String, String> map) {
// 过期时间为七天
Calendar instance = Calendar.getInstance();
instance.add(Calendar.DATE, 7);
// 创建JWT builder
JWTCreator.Builder builder = JWT.create();
// 存入信息payload中信息(也就是map中的信息)
map.forEach((k, v) -> builder.withClaim(k, v));
// 存入过期时间和密钥
String token = builder.withExpiresAt(instance.getTime())
.sign(Algorithm.HMAC256(SIGN));
return token;
}
/**
* 验证token 如果正确则返回token信息
*/
public static DecodedJWT verify(String token) {
// 如果token不对会直接抛出异常
return JWT.require(Algorithm.HMAC256(SIGN)).build().verify(token);
}
}
JWT拦截器
/**
* 避免每次都手动穿参数,我们直接写一个拦截器
*/
public class JWTInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
Map<String, Object> map = new HashMap<>();//实际业务中应该是Result实体类
//从请求头中获取令牌
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.put("state",false);//设置状态为验证失败
//前后端分离需要返回信息是json格式的 但是不在Controller中,无法自动将map转为为json
// 所以使用jackson手动转换
String json = new ObjectMapper().writeValueAsString(map);
response.setContentType("application/json;charset=UTF-8");
response.getWriter().println(json);
return false;
}
}
拦截器配置
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new JWTInterceptor())
.addPathPatterns("/**") //需要token验证的接口 一般是全部接口
.excludePathPatterns("/user/**"); //用户接口都需要放行,因为登陆和注册是还没有token,如果此时验证token则肯定不会成功
}
}
Controller层
@RestController
@Slf4j
public class UserController {
@Autowired
private UserService userService;
/**
* 生成token 返回给前段 自动存储在header中
*/
@GetMapping("/user/login")
public Map<String,Object> login(User user){
log.info("用户名: [{}]",user.getName());
log.info("密码: [{}]",user.getPassword());
Map<String, Object> map = new HashMap<>(); // 实际业务中会使用一个Result实体类
try{
User userDB = userService.login(user);
Map<String,String> payload = new HashMap<>(); //存储用户信息用于生成token
payload.put("id",userDB.getId());
payload.put("name",userDB.getName());
String token = JWTUtils.getToken(payload); // 生成JWT的令牌
map.put("state",true);
map.put("msg","认证成功");
map.put("token",token);//响应token
}catch (Exception e){
map.put("state",false);
map.put("msg",e.getMessage());
}
return map;
}
/**
* 业务需要使用token中的用户信息是,通过request参数获得token
*/
@PostMapping("/user/test")
public Map<String,Object> test(HttpServletRequest request){
Map<String, Object> map = new HashMap<>(); // 实际业务中会使用Result实体类
/* 自己的业务逻辑 */
String token = request.getHeader("token"); // 获取token
// 验证token 必然是成功的,不然是无法进入这个路由的,这步的主要目的是获取DecodedJWT对象
// 这步完全可以替换成 DecodedJWT verify = JWTUtils.decode(token);
DecodedJWT verify = JWTUtils.verify(token);
log.info("用户id: [{}]",verify.getClaim("id").asString()); // 通过getClaim获取想要的信息
log.info("用户name: [{}]",verify.getClaim("name").asString());
map.put("state",true);
map.put("msg","请求成功!");
return map;
}
}