一、拦截器

  • return true:表示放行请求;
  • return false:表示拦截;
  1. 配置类:实现WebMvcConfigurer 接口的addInterceptors方法,向springboot中添加具体的拦截器
@Configuration
public class MvcConfig implements WebMvcConfigurer {
    //添加拦截器
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LoginInterceptor())
                .excludePathPatterns(
                        "/user/code",
                        "/user/login"); //不拦截这两个请求
    }
}
  1. 拦截器具体实现:重写handlerInterceptor接口中的preHandleafterCompletion等方法
public class LoginInterceptor implements HandlerInterceptor {
    //前置拦截
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
       //获取session
        HttpSession session = request.getSession();
        //获取session中的用户
        Object user = session.getAttribute("user");
        //如果用户存在不存在拦截
        if(user==null) {
            response.sendError(401,"用户为登录");
            return false; //false就表示拦截
        }
        //将每个用户的信息存到ThreadLocal中
        UserHolder.saveUser((UserDTO) user);
        return true; //表示放行
    }

    //后置拦截,注销后清楚session
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        UserHolder.removeUser();
    }
}
public class UserHolder {
    private static final ThreadLocal<UserDTO> tl = new ThreadLocal<>();
    public static void saveUser(UserDTO user){
        tl.set(user);
    }
    public static UserDTO getUser(){
        return tl.get();
    }
    public static void removeUser(){
        tl.remove();
    }
}

二、过滤器

  • filterChain.doFilter(request,response): 表示放行;
  • 如果请求被阻止,doFilter方法的返回值是void,因此respone可以通过输出流像前端返回json数据,即,如: response.getWriter().write(JSON.toJSONString(R.error("NOTLOGIN")));
  1. 过滤器实现:实现Filter接口的doFilter方法
@WebFilter(filterName = "LoginCheckFilter",urlPatterns = "/*")
@Slf4j
public class LoginCheckFilter implements Filter {
    public static final AntPathMatcher antPathMatcher = new AntPathMatcher();
    @Override
    public void doFilter(ServletRequest servletRequest,
                         ServletResponse servletResponse,
                         FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        
        //1. 获取本次请求的URI
        String requestURI=request.getRequestURI();
        log.info("拦截到请求:{}",requestURI);
        
        //2. 定义直接放行的请求
        String[] urls=new String[]{
               "/employee/login", //动态资源
                "/employee/logout",//动态资源
               "/backend/**",  //静态资源
               "/front/**", //静态资源
                "/common/**"//动态资源
        };
        boolean isCheck=check(urls,requestURI);
        
        //3. 上面的这些请求都可以放行
        if(isCheck){
            log.info("本次请求{}不需要处理",requestURI);
            filterChain.doFilter(request,response); //这就是放行
            return;
        }
        
        //4. 不是直接放行的请求,首先需要判断是否登录,登录成功则放行,因为第一次登录成功后会分配一个sessionID
        if(request.getSession().getAttribute("employee")!=null){
            log.info("用户已登录,用户id:{}",request.getSession().getId());
            //客户端请求时候,服务器会为该请求分配一个线程,
            // 因为我们请求是异步请求,所以我们登录成功后的所有请求都是在一个线程里处理的,
            //这里我们用线程可以存储用户id,是为了后面MyBatisPlus的字段自动填充时,自动填充createUser和updateUser的。
            Long id=(Long) request.getSession().getAttribute("employee");
            BaseContext.setCurrentId(id);
            filterChain.doFilter(request,response);
            return;
        }
        
        //5. 如果未登录,返回登录结果
        //因为该方法是void,没有返回值,因此是通过输出流像前端返回json数据
        log.info("用户未登录{}",requestURI);
        response.getWriter().write(JSON.toJSONString(R.error("NOTLOGIN")));
        return;
    }

    //进行匹配
    public boolean check(String[] urls,String requestURI){
        for (String url : urls) {
          boolean match = antPathMatcher.match(url,requestURI);
          if(match) return true;
        }
        return false;
    }
}
  1. 启动类上还需要加上扫描注解@ServletComponentScan
@SpringBootApplication
@ServletComponentScan //扫描过滤器的注解
@EnableTransactionManagement(proxyTargetClass = true) //开始事务注解功能,对数据库操作的那些事务
public class TakeOutApplication {
    public static void main(String[] args) {
        SpringApplication.run(TakeOutApplication.class, args);
    }
}

三、异常处理器

  1. 异常处理器:基于@ExceptionHandler注解实现。
//捕获那些加RestController和Controller注解的类上出现的异常
@ControllerAdvice(annotations = {RestController.class, Controller.class})
@ResponseBody
@Slf4j
public class GlobalException {
    @ExceptionHandler(SQLIntegrityConstraintViolationException.class) //捕获这种类型的异常
    public R<String>  ExceptionHandler(SQLIntegrityConstraintViolationException ex){
        log.info(ex.getMessage());

        //根据异常的具体情况进行处理
        if(ex.getMessage().contains("Duplicate entry")){
            String[] strings = ex.getMessage().split(" ");
            String msg=strings[2]+"已存在";
            return R.error(msg);
        }
        return R.error("未知错误");
    }

    @ExceptionHandler(SQLSyntaxErrorException.class)
    public R<String>  ExceptionHandler(SQLSyntaxErrorException ex){
        log.info(ex.getMessage());
        return R.error("SQLSyntaxErrorException异常");
    }
 }