Skip to content

spring security 集成 jwt

GoldSubmarine edited this page Sep 10, 2019 · 1 revision

集成主要参考:https://github.com/szerhusenBC/jwt-spring-security-demo

但在jwt验证上做了一些修改,也遇到一些问题

每个用户都有一个jwt加密私钥

项目使用jjwt这个库做加密和验证,我在user表中添加了一个jwtSecert字段,用于存放每个用户自己的jwt私钥,而不是所有用户使用同一个私钥,一方面这样更安全,一个用户被暴力破解不会影响其他用户,另一个主要原因是jwt没有撤销签发token的机制,一旦签发token,那在过期时间之前token一直有效,虽然前端退出登录自行删除了token,但是这是一种假注销,我们需要一种真注销的方式,那就是改变这个用户的私钥。

假设用户分别登录了两台电脑,其中一台电脑退出登录,另一台电脑还是应该能正常使用,所以进行退出登录的操作时,不应该刷新用户的私钥。只有在用户更改密码时,才应该刷新私钥,使之前签发的所有token都失效。

无法获取用户标识的问题

前端传递一个token过来后,需要使用jjwt解密才能获取到token里的信息,虽然token只要base64转换就能获取到信息,但是不经过密钥去获取信息是不安全的。我们的私钥在数据库中,所以要查库,但是没有私钥就不能从token中获取到用户的唯一标识,也就不能查库。

jjwt提供了除了直接使用密钥解密,还提供了一种允许解密前获取token中信息的方法,参见文档

filter 中的异常无法被 @ControllerAdvice 捕获的问题

filter 不属于 spring 的一部分,并且也不在 controller 中,所以无法被@ControllerAdvice 捕获,一个小技巧是在 filter 中 try catch,捕获到异常后,通过 HttpServletRequest 将请求转发到一个专门的异常处理 controller 中,在这个 controller 中抛出异常。示例如下:

// MyFilter.java
@Component
public class MyFilter extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
        try {
            throw new RuntimeException();
            chain.doFilter(request, response);
        } catch (Exception e) {
            // 将异常缓存成request的一个属性
            request.setAttribute(JwtAuthFilter.class.getSimpleName(), e);
            // 过滤器的异常不能被RestControllerAdvice捕获到,跳转到专门的异常controller
            request.getRequestDispatcher("/filterExceptionHandler").forward(request, response);
        }
    }

}
// ExceptionController.java
@Controller
public class ExceptionController {
    /**
     * 直接throw Filter 传过来的异常,让 ControllerAdvice 处理
     */
    @RequestMapping("/filterExceptionHandler")
    public void filterExceptionHandler(HttpServletRequest request) throws Throwable {
        Exception e = (Exception) request.getAttribute(JwtAuthFilter.class.getSimpleName());
        if (e != null) {
            throw e;
        }
    }
}