一、具体操作
具体参照这篇
二、几点补充
1、引入Jwt依赖
io.jsonwebtoken
jjwt
0.7.0
2、在filter中验证token时,过期和非法的token都会抛出异常,可以自定义bean继承自BasicErrorController来进行统一的异常处理(返回给前端固定的Json内容,实际使用时和Js交互还会遇到跨域问题,要给response加上相关的请求头)。
3、贴波自己写的代码
注:基于 boot 1.X, 2.X的BasicErrorController有所不同
不懂的留言
@Configuration
public class JwtConfig {
@Bean
public FilterRegistrationBean jwtFilter() {
final FilterRegistrationBean registrationBean = new FilterRegistrationBean();
registrationBean.setFilter(new JwtFilter());
registrationBean.addUrlPatterns("/test/*");//配置对应路径的接口使用此 filter
return registrationBean;
}
}
@RestController
public class MyCommonErrorController extends BasicErrorController {
//统一处理filter抛出的token相关的异常 返回给前端标准格式的json和装填码
private static final String PATH = "/error";
private static final String TOKEN_MISS = "Missing or invalid Authorization header";
private static final String TOKEN_EXPIRED = "token expired";
private static final String TOKEN_INVALID = "token invalid";
private static final String TOKEN_ERROR = "error";
public MyCommonErrorController() {
super(new DefaultErrorAttributes(), new ErrorProperties());
}
@Override
@RequestMapping(
produces = {"application/json"}
)
public ResponseEntity
@RestController
public class TokenController {
//登陆接口
@PostMapping("/login")
public Object login(@RequestBody LoginRequest loginRequest){
//假设验证过了用户名和密码 发token
// Create Twt token
return generateToken(loginRequest.getUsername());
}
private String generateToken(String username) {
Map claims = new HashMap<>(16);
claims.put("sub", username);
claims.put("created", new Date());
return generateToken((claims));
}
private String generateToken(Map claims) {
return Jwts.builder().setClaims(claims) //payload
.setExpiration(new Date(System.currentTimeMillis() + 60 * 1000L)) //过期时间
.signWith(SignatureAlgorithm.HS512, "nicai").compact(); //加密方式
}
}
@RestController
@RequestMapping("/test")
public class TestController {
//用于测试token的验证
@GetMapping("")
public Object getTest(HttpServletRequest request){
System.out.println(request.getAttribute("claims"));
Map claims = (Map) request.getAttribute("claims");
return "test";
}
}
@Data
public class TokenException extends Exception {
//自定义异常类型
private int code;
private String msg;
public TokenException() {
}
public TokenException(int code, String msg) {
super(msg);
this.code = code;
this.msg = msg;
}
}
public class JwtFilter extends GenericFilterBean{
//Jwtconfig中配置的filter 用于Jwt token的验证工作 配置时可以指定对应的路径
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
final HttpServletResponse response = (HttpServletResponse) servletResponse;
final HttpServletRequest request = (HttpServletRequest) servletRequest;
String authHeader = request.getHeader("Authorization");
//规避探测性质的 OPTIONS请求
String optionsString = "OPTIONS";
String bearerString = "Bearer ";
if (optionsString.equals(request.getMethod())){
response.setStatus(HttpServletResponse.SC_OK);
filterChain.doFilter(servletRequest, servletResponse);
}else {
//验证token
if (StringUtils.isEmpty(authHeader) || !authHeader.startsWith(bearerString)){
throw new ServletException(new TokenException(-1, "Missing or invalid Authorization header"));
}else {
String token = authHeader.substring(bearerString.length());
try {
//使用jwt paser来验证签名
Claims claims = Jwts.parser().setSigningKey("nicai").parseClaimsJws(token).getBody();
request.setAttribute("claims", claims);
}catch (ExpiredJwtException e){
throw new ServletException(new TokenException(-2, "token expired"));
}catch (SignatureException e){
throw new ServletException(new TokenException(-3, "token invalid"));
}catch (Exception e){
throw new ServletException(new TokenException(-4, "error"));
}
}
filterChain.doFilter(servletRequest, servletResponse);
}
}
}