Java中拦截器与过滤器的区别及各自功能的实现

前言

本篇博文主要是想表达以下几个方面

  • 什么是过滤器?
  • 什么是拦截器?
  • 过滤器与拦截器的区别在于什么地方?
  • 过滤器与拦截器各自应用的场景是什么?
  • 用过滤器与拦截器实现实际开发中的功能需求

什么是过滤器-Filter

前人言:取你所想

  • 依赖于Servlet容器,实现基于回调函数,对所有的请求进行过滤
  • Filter随web应用的启动而启动,只初始化一次,随web应用的停止而销毁。
    • 启动时加载过滤器的实例,并调用**init()**方法来初始化实例
    • 每次的请求只能调用**doFilter()**进行处理
    • 当服务停止时,调用destory销毁实例

缺点

  • 只能是在容器初始化时调用一次

什么是拦截器-Interceptor

前人言:用你所用

  • 依赖于web框架,依赖于Java的反射机制,是AOP的思想体现
  • 基于web框架,可以调用IOC容器中的各种依赖,可以进行一些业务操作
  • 实现拦截器的俩种方式(spring)
    • 实现HandlerInterceptor接口
    • 实现WebRequestInterceptor接口 或者 实现 WebRequestInterceptor 的类
  • HandlerInterceptor接口定义方法preHandle, postHandle, 和afterCompletion
    • preHandle:请求方法前置拦截,该方法会在Controller处理之前进行调用,Spring中可以有多个Interceptor,这些拦截器会按照设定的Order顺序调用,当有一个拦截器在preHandle中返回false的时候,请求就会终止
    • postHandle:preHandle返回结果为true时,在Controller方法执行之后,视图渲染之前被调用
  • afterCompletion:在preHandle返回ture,并且整个请求结束之后,执行该方法。

过滤器与拦截器的区别

相同

  • 都是aop思想的体现
  • 都是对请求的方法进行拦截,对方法进行增强

不同

  • 实现原理不同

    • Filter基于回调函数
    • Interceptor基于Java的反射机制(动态代理实现)
  • 使用范围不同

    • Filter在Servlet规范中定义的,依赖于Tomcat等容器,只能在web的程序中使用
    • Interceptor 是spring的组件,由spring管理,可以单独使用
  • 触发时机不一样

    img/image-20211115101325025.png  0 → 100644

扫描二维码关注公众号,回复: 13923892 查看本文章

拦截器的功能实现

功能需求: 实现用户的登录验证

拦截器的配置类(InterceptorConfig)

  • 一般主要是用来配置拦截的路径/放行的路径
  • 注入Bean,在发生拦截时生效.举例: 下面的LoginInterceptor
/**
 * 拦截配置
 */
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
    
    
    @Bean
    LoginInterceptor loginInterceptor() {
    
    
        return new LoginInterceptor();
    }

    /**
     * 添加拦截路径
     * 放行拦截路径
     * @param registry
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
    
    

        // 拦截全部路径,这个跨域需要放在最上面
        registry.addInterceptor(corsInterceptor()).addPathPatterns("/**");

        // 拦截路径
        registry.addInterceptor(loginInterceptor()).addPathPatterns("/api/v1/pri/*/*/**")
                //放行路径
                .excludePathPatterns("/api/v1/pri/user/login", "/api/v1/pri/user/register");
        WebMvcConfigurer.super.addInterceptors(registry);
    }
}

实现拦截器

  • 实现HandlerInterceptor接口,重写里面preHandle的方法

  • 这里实现一个用户权限校验的过程,前端向后端发送请求的时候,拦截请求,获取里面的token,将"破译"后的token 放入request域中返回

  • sendJsonMessage类,当校验token为null时直接response响应给前端

/**
 * 登录拦截器
 * 进入到Restcontroller之前的方法
 * @author ouj
 */
public class LoginInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        try {
            //从请求中获取token
            String accessToken = request.getHeader("token");
            if (accessToken == null) {
                accessToken = request.getParameter("token");
            }
            // 判断token是否为null
            if (StringUtils.isNotBlank(accessToken)) {
                Claims claims = JWTUtils.checkJWT(accessToken);
                if (claims == null) {
                    sendJsonMessage(response, "登录过期,请重新登录");
                }
                Integer id = (Integer) claims.get("id");
                String name = (String) claims.get("name");

                //在request域中把id的属性放进"User_id"中
                request.setAttribute("user_id", id);
                request.setAttribute("name", name);
                return true;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        // 登录失败
        return false;
    }

    /**
     * 响应json数据给前端
     * @param response
     * @param obj
     */
    public static void sendJsonMessage(HttpServletResponse response, Object obj) {

        try {
            /**
             * 将数据转成Json字符串,然后以数据流的形式writer写出去
             * ObjectMapper 是Jackson库中主要用于读取和写入Json数据的类,能够很方便地将Java对象转为Json格式的数据
             * 用于后端Servlet向AJAX传递Json数据,动态地将数据展示在页面上。为了能够使用这个类,需要先下载Jackson库。
             */
            ObjectMapper objectMapper = new ObjectMapper();
            response.setContentType("application/json; charset=utf-8");
            //返回一个输出流
            PrintWriter writer = response.getWriter();
            // writeValue(参数,obj):直接将传入的对象序列化为json,并且返回给客户端
            // writeValueAsString(obj):将传入的对象序列化为json,返回给调用者
            writer.print(objectMapper.writeValueAsString(obj));
            writer.close();
            response.flushBuffer();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
    }
}

过滤器的功能实现

实现功能需求:用户校验

/**
 * 过滤器
 */
@WebFilter(urlPatterns = "/api/v1/pri/*",filterName = "loginfilter")
public class LoginFilter implements Filter {
    private static final ObjectMapper objectMapper = new ObjectMapper();
    /**
     * 容器加载的时候
     * @param filterConfig
     * @throws ServletException
     */
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("开启过滤器");
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("过滤开始");
        HttpServletRequest req = (HttpServletRequest) servletRequest;
        HttpServletResponse rep = (HttpServletResponse) servletResponse;

        //从headers中得到token
        String token =req.getHeader("token");
        // 如果header里面没有token时,从请求的参数里面拿token
        if (StringUtils.isEmpty(token)){
            token = req.getParameter("token");
        }
        //如果还为空,直接返回json字符串或跳转index页面
        if (!StringUtils.isEmpty(token)){
            // 判断token是否合法
            //在登录时在sessionMap里存了token,通过token取出user,若为空则未登录,若不为空则显示正确
            User user = UserServiceImpl.sessionMap.get(token);
            if(user !=null){
                filterChain.doFilter(servletRequest,servletResponse);
            }else{
                JsonData jsonData = JsonData.buildError("登录失败,token无效",-2);
                //json转字符
                String jsonStr = objectMapper.writeValueAsString(jsonData);
                renderJson(rep,jsonStr);
            }
        }else {
            JsonData jsonData = JsonData.buildError("登录失败,token无效",-3);
            String jsonStr = objectMapper.writeValueAsString(jsonData);
            renderJson(rep,jsonStr);
        }
    }

    private void renderJson(HttpServletResponse response,String json){
        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/json");
        try(PrintWriter writer=response.getWriter()){
            writer.print(json);
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    /**
     * 容器销毁的时候
     */
    @Override
    public void destroy() {
        System.out.println("destory LoginFilter");
    }
}

应用场景

  • 过滤器
    • 字节码编码转换
    • 敏感词过滤 (列入防止sql注入)
    • 权限验证
    • 压缩响应信息
  • 拦截器
    • 权限验证
    • 日志记录
    • 性能监控
    • 通用行为
      • 读取cookie得到用户信息并将用户对象放入请求,从而方便后续流程使用,还有如提取Locale、Theme信息等,只要是多个处理器都需要的即可使用拦截器实现5)

猜你喜欢

转载自blog.csdn.net/m0_49969111/article/details/121332863