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

農碼關山北發表於2019-07-31

概述

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

 @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反射機制實現,過濾器是函式的回撥。因此實現方式是不同的。

三者使用場景

  監聽器常用統計線上使用者,統計網站的訪問量,記錄使用者的訪問路徑

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

  攔截器:許可權驗證,判斷使用者是否登入等

 

相關文章