springBoot的過濾器,監聽器,攔截器

  • 2019 年 10 月 3 日
  • 筆記

概述

  在開發中,我們經常要考慮一些問題,對敏感詞進行過濾,用戶是否已經登錄,是否需要對他的請求進行攔截,或者領導問現在在線人數有多少人?我們如何實現這些功能哪

 @WebFilter

package com.xmlxy.firstspringbootproject;      import org.slf4j.Logger;  import org.slf4j.LoggerFactory;    import javax.servlet.*;  import javax.servlet.annotation.WebFilter;  import java.io.IOException;  @WebFilter(filterName = "customFilter",urlPatterns = "/*")  public class CustomFilter implements Filter {        private static final Logger log = LoggerFactory.getLogger(CustomFilter.class);        @Override      public void init(FilterConfig filterConfig) throws ServletException {          log.info("===========攔截器初始化==========");      }        @Override      public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {          servletRequest.setCharacterEncoding("utf-8");          servletResponse.setCharacterEncoding("utf-8");          log.info("doFilter請求處理");      }        @Override      public void destroy() {          log.info("fifter銷毀");      }  }

在application類中添加@ServletComponentScan註解

package com;    import org.springframework.boot.SpringApplication;  import org.springframework.boot.autoconfigure.SpringBootApplication;  import org.springframework.boot.web.servlet.ServletComponentScan;  import org.springframework.context.annotation.PropertySource;    @SpringBootApplication  @ServletComponentScan  @PropertySource(value = "classpath:jdbc.properties",encoding = "utf-8")  public class FirstSpringbootProjectApplication {        public static void main(String[] args) {          SpringApplication.run(FirstSpringbootProjectApplication.class, args);      }    }

運行結果

(筆誤,應該是過濾器初始化)過濾器已經生效,但若有多個過濾器,無法指定執行順序,我們可以通過Java類的名稱,從A-L,按順序執行。但這種方式畢竟不大靠譜,所以,有第二種寫法,它提供setOrder函數,為filter設置排序值。

package com.xmlxy.service;    import com.xmlxy.firstspringbootproject.CustomFilter;  import org.springframework.boot.web.servlet.FilterRegistrationBean;  import org.springframework.context.annotation.Bean;  import org.springframework.context.annotation.Configuration;    @Configuration  public class WebFilterConfig  {      @Bean      public FilterRegistrationBean someFilterRegistration1()      {          FilterRegistrationBean registration = new FilterRegistrationBean<>();          System.out.println("我執行了。。。。。。。");          registration.setFilter(new CustomFilter());          registration.addUrlPatterns("/*");
     registration.setOrder(1);
     return registration; } }

我們嘗試寫個demo,驗證一下過濾器是否執行。

用戶登錄對象

User.java

package com.xmlxy.bean;    import lombok.Data;  import org.springframework.stereotype.Component;    @Data  @Component  public class User {      private String user;      private String pwd;  }

登錄控制

LoginController.java

package com.xmlxy.firstspringbootproject;    import com.xmlxy.bean.User;  import org.springframework.web.bind.annotation.RequestMapping;  import org.springframework.web.bind.annotation.RequestMethod;  import org.springframework.web.bind.annotation.RestController;    import javax.servlet.http.HttpServletRequest;  import javax.servlet.http.HttpSession;    @RestController  public class LoginController {        @RequestMapping(value = "login",method = RequestMethod.GET)      public String login(HttpServletRequest request)      {          String user = request.getParameter("user");          String pwd = request.getParameter("pwd");          HttpSession session = request.getSession();          if ("admin".equals(user) && "admin".equals(pwd))          {              User user1 = new User();              user1.setUser(user);              user1.setPwd(pwd);              session.setAttribute("user",user1);              return "登錄成功";          }          return "密碼錯誤,登錄失敗";      }
  @RequestMapping(value = "test",method = RequestMethod.GET)
  public String test()
  {
  return "test介面";
  }
}

過濾器

CustomFilter.java

package com.xmlxy.firstspringbootproject;      import org.slf4j.Logger;  import org.slf4j.LoggerFactory;    import javax.servlet.*;  import javax.servlet.annotation.WebFilter;  import javax.servlet.http.HttpServletRequest;  import javax.servlet.http.HttpServletResponse;  import javax.servlet.http.HttpSession;  import java.io.IOException;  //@WebFilter(filterName = "customFilter",urlPatterns = "/*")  public class CustomFilter implements Filter {        private static final Logger log = LoggerFactory.getLogger(CustomFilter.class);        String includes[] = {"/login","register"};        @Override      public void init(FilterConfig filterConfig) throws ServletException {          log.info("===========過濾器初始化==========");      }        @Override      public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException      {          HttpServletRequest request = (HttpServletRequest) servletRequest;          HttpServletResponse response = (HttpServletResponse) servletResponse;          servletRequest.setCharacterEncoding("utf-8");          servletResponse.setCharacterEncoding("utf-8");          HttpSession session = request.getSession(false);          String uri = request.getRequestURI();          boolean flag = isNeedFilter(uri);          if (!flag)          {              filterChain.doFilter(servletRequest,servletResponse);              System.err.printf("登錄成功");          }else {              if (session != null && session.getAttribute("user") != null)              {                  filterChain.doFilter(servletRequest,servletResponse);              }else {                  System.err.printf("暫時未登錄");              }          }            log.info("doFilter請求處理");      }      public boolean isNeedFilter(String uri)      {          for (String include:includes)          {              if (include.equals(uri))              {                  return false;              }          }          return true;      }      @Override      public void destroy() {          log.info("fifter銷毀");      }  }

過濾器配置

WebFilterConfig

package com.xmlxy.service;    import com.xmlxy.firstspringbootproject.CustomFilter;  import org.springframework.boot.web.servlet.FilterRegistrationBean;  import org.springframework.context.annotation.Bean;  import org.springframework.context.annotation.Configuration;    @Configuration  public class WebFilterConfig  {      @Bean      public FilterRegistrationBean someFilterRegistration1()      {          FilterRegistrationBean registration = new FilterRegistrationBean<>();          System.out.println("我執行了。。。。。。。");          registration.setFilter(new CustomFilter());          registration.addUrlPatterns("/*");          return registration;      }  }

 運行測試。。訪問 127.0.0.1/test  控制台

訪問  http://127.0.0.1:8080/login?user=admin&pwd=admin,可以看到登錄成功

在次訪問 127.0.0.1/test 頁面顯示

所以,我們的過濾器成功過濾未登錄的用戶

監聽器

正在你為自己會了過濾用戶自鳴得意時,你的組長過來了,小明,你看下我們平台的在線用戶有多少人。如果不知道監聽器童鞋,是否會在登錄介面處每次登錄成功都+1,然而這種統計結果是不準確的,因為用戶如果反覆登錄退出,那這個在值就遠遠大於實際值,最後就面臨著,加班在加班的悲慘下場。

CustomLister.java

package com.xmlxy.firstspringbootproject;      import javax.servlet.annotation.WebFilter;  import javax.servlet.http.HttpSessionEvent;  import javax.servlet.http.HttpSessionListener;    @WebFilter  public class CustomLister implements HttpSessionListener  {      public static int online = 0;        @Override      public void sessionCreated(HttpSessionEvent sessionEvent)      {          System.out.println("創建session,統計在線人數");          online ++;      }        @Override      public void sessionDestroyed(HttpSessionEvent sessionEvent)      {          System.out.println("session已經銷毀");      }  }

配置監聽器配置,在剛才的WebFilterConfig.java添加

@Bean      public ServletListenerRegistrationBean listenerRegistrationBean()      {          ServletListenerRegistrationBean srb = new ServletListenerRegistrationBean();          srb.setListener(new CustomLister());          System.out.println("");          return srb;      }

添加一個訪問在線人數的介面

@RequestMapping(value = "onLinePerson",method = RequestMethod.GET)      public String onLinePerson()      {          StringBuffer stringBuffer = new StringBuffer("");          stringBuffer.append(" 在線人數 ");          stringBuffer.append(CustomLister.online);          stringBuffer.append(" 個人 ");          return stringBuffer.toString();      }

訪問127.0.0.1/onLinePerson,發現被攔截器攔截了,我們先登錄。在查看介面

換個瀏覽器,調用下login介面,在查看

攔截器

攔截器,個人理解,在web上有些像是過濾器的補充,它能更精確的控制攔截哪些函數或者欄位,在攔截之前或之後做一些操作。我們現在做一個敏感詞的攔截,其實這個操作放在過濾器操作也是可以的,但lz因為剛才把攔截用戶的操作放在過濾器了,在大規模更改,lz覺得沒必要,因為都是大同小異。

CustomInterceptor.java

package com.xmlxy.firstspringbootproject;    import org.slf4j.Logger;  import org.slf4j.LoggerFactory;  import org.springframework.web.servlet.HandlerInterceptor;  import org.springframework.web.servlet.ModelAndView;  import javax.servlet.http.HttpServletRequest;  import javax.servlet.http.HttpServletResponse;  import java.io.PrintWriter;    public class CustomInterceptor implements HandlerInterceptor  {      private static final Logger log = LoggerFactory.getLogger(CustomInterceptor.class);      @Override      public boolean preHandle(HttpServletRequest request, HttpServletResponse response,Object handler) throws Exception {          log.info("=======攔截器被調用=====");          String url = request.getRequestURI();          if (url != null && url.indexOf("seqing") != -1)          {              PrintWriter printWriter = response.getWriter();              printWriter.write("ming gan ci");              return false;          }          log.info("返回false 則中斷請求");          return true;      }        @Override      public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)      {          log.info("請求後調用");      }        @Override      public void afterCompletion(HttpServletRequest request,HttpServletResponse response,Object handler,Exception e)      {          log.info("視圖渲染完回調");      }  }

配置攔截

package com.xmlxy.service;    import com.xmlxy.firstspringbootproject.CustomFilter;  import com.xmlxy.firstspringbootproject.CustomInterceptor;  import com.xmlxy.firstspringbootproject.CustomLister;  import org.springframework.boot.web.servlet.FilterRegistrationBean;  import org.springframework.boot.web.servlet.ServletListenerRegistrationBean;  import org.springframework.context.annotation.Bean;  import org.springframework.context.annotation.Configuration;  import org.springframework.web.servlet.config.annotation.InterceptorRegistry;  import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;    @Configuration  public class WebFilterConfig implements WebMvcConfigurer  {      @Bean      public FilterRegistrationBean someFilterRegistration1()      {          FilterRegistrationBean registration = new FilterRegistrationBean<>();          System.out.println("我執行了。。。。。。。");          registration.setFilter(new CustomFilter());          registration.addUrlPatterns("/*");          return registration;      }        @Bean      public ServletListenerRegistrationBean listenerRegistrationBean()      {          ServletListenerRegistrationBean srb = new ServletListenerRegistrationBean();          srb.setListener(new CustomLister());          return srb;      }        @Override      public void addInterceptors(InterceptorRegistry registry)      {          /*攔截規則*/          registry.addInterceptor(new CustomInterceptor()).addPathPatterns("/*");      }    }

現在測試訪問127.0.0.1/seqing,被過濾,要求先登錄。我們調用登錄介面後,再次調用,發現被攔截了

看下日誌調用,可以發現,攔截器是在訪問介面前被調用的

過濾器,攔截器區別

這裡主要說下攔截器和過濾器的區別和使用場景,通過demo可以發現,它們都能實現許可權的檢查,日誌記錄這些功能,主要說下它們的區別

  1. 過濾器和攔截器觸發的時機是不同的,在進入servlet之前,過濾器就進行預處理了。而攔截器是在調用Controller之前才觸發執行,過濾器的範圍較廣,對所有的請求都起作用,而攔截起只 對action起作用

  2.攔截器可以獲取IOC容器的各個bean,而過濾器就不行。因為攔截器是spring提供管理的,也因此攔截器可以使用spring的任何資源。

  3.攔截器是利用Java反射機制實現,過濾器是函數的回調。因此實現方式是不同的。

三者使用場景

  監聽器常用統計在線用戶,統計網站的訪問量,記錄用戶的訪問路徑

  過濾器:過濾敏感詞,許可權訪問控制

  攔截器:許可權驗證,判斷用戶是否登錄等