springBoot的过滤器,监听器,拦截器

概述


  在开发中,我们经常要考虑一些问题,对敏感词进行过滤,用户是否已经登录,是否需要对他的请求进行拦截,或者领导问现在在线人数有多少人?我们如何实现这些功能哪

 @WebFilter

package com.net.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.net.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.net.bean;

import lombok.Data;
import org.springframework.stereotype.Component;

@Data
@Component
public class User {
    private String user;
    private String pwd;
}

登录控制

LoginController.java

package com.net.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.net.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.net.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 页面显示

所以,我们的过滤器成功过滤未登录的用户

 

监听器

先写个HttpSessionListener 监听器。count  是session的数量(人数),session 创建的时候,会触发监听器的sessionCreated 方法,session销毁的时候,会触发监听器的sessionDestroyed 方法。 在监听器中计算完人数count,把他放进servletContext(可以理解为一个仓库,任意请求可以存储和获取里面的属性)。

 注意监听器加上@WebListener,这样就不用配置。

@WebListener

public class CustomLister implements HttpSessionListener {

    public int count=0;//记录session的数量

    //监听session的创建,synchronized 防并发bug

    public synchronized void sessionCreated(HttpSessionEvent arg0) {
        System.out.println("【HttpSessionListener监听器】count++  增加");
        count++;
        arg0.getSession().getServletContext().setAttribute("count", count);

    }

    @Override
    public synchronized void sessionDestroyed(HttpSessionEvent arg0) {//监听session的撤销
        System.out.println("【HttpSessionListener监听器】count--  减少");
        count--;
        arg0.getSession().getServletContext().setAttribute("count", count);

    }
}

接着写一个查询session 数量的controller,我开始的时候是像下面这样写的,是错误的!

从servletContext 中取出count ,把count返回前端。

@RequestMapping("/count")
@ResponseBody
public String count(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse){
    Object count=httpServletRequest.getServletContext().getAttribute("count");
    return "count : "+count;

}

这样是错误的,测试你会发现,页面看到count  是null ,因为没有创建session,没有触发监听器的统计方法。于是改一下:

@Controller
public class IndexController {
    @RequestMapping("/count")
    @ResponseBody
    public String count(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse){
        HttpSession session = httpServletRequest.getSession();
        Object count=session.getServletContext().getAttribute("count");
        return "count : "+count;
    }
}

HttpSession session = httpServletRequest.getSession();  作用:该用户如果没有sesision则创建session ,有则取得session不创建。

 改成这样测试,看起来是对的,但是有个问题。一个浏览器对应一个session,你打开2个浏览器,看到count是2 ,是对的。但是你关了一个浏览器,再打开,应该是2不变才对,但是变成3 了,原因是session销毁的方法没有执行,重新打开时,服务器找不到用户原来的session ,重新创建了一个session,于是有3个session了,但是浏览器只有2个,也就是模拟应该是只有2个人在线上。

 有2个方法可以解决这个问题,一个是在关闭网页的时候,前端去调用一个方法把session销毁。另一个更好的方法是,让服务器记得原来那个session,即把原来的sessionId 记录在浏览器,下次打开时,把这个sessionId发送过去,这样服务器就不会重新创建。

代码修改如下:

@Controller
public class IndexController {
    @RequestMapping("/count")
    @ResponseBody
    public String number(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse){

        try{  //把sessionId记录在浏览器
            Cookie c = new Cookie("JSESSIONID", URLEncoder.encode(httpServletRequest.getSession().getId(), "utf-8"));
            c.setPath("/");
            //先设置cookie有效期为2天,不用担心,session不会保存2天
            c.setMaxAge( 48*60 * 60);
            httpServletResponse.addCookie(c);
        }catch (Exception e){
            e.printStackTrace();
        }

        HttpSession session = httpServletRequest.getSession();
        Object count=session.getServletContext().getAttribute("count");
        return "count : "+count;
    }
}

测试达到效果。

监听器的另一种配置方式

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

 

拦截器


拦截器,个人理解,在web上有些像是过滤器的补充,它能更精确的控制拦截哪些函数或者字段,在拦截之前或之后做一些操作。我们现在做一个敏感词的拦截,其实这个操作放在过滤器操作也是可以的,但lz因为刚才把拦截用户的操作放在过滤器了,在大规模更改,lz觉得没必要,因为都是大同小异。

CustomInterceptor.java

package com.net.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.net.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反射机制实现,过滤器是函数的回调。因此实现方式是不同的。

三者使用场景
 

监听器:常用统计在线用户,统计网站的访问量,记录用户的访问路径

过滤器:过滤敏感词,权限访问控制

拦截器:权限验证,判断用户是否登录等

 

附:前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,分享一下给大家。点击跳转到教程。

--- end ---

 

你可能感兴趣的:(Java)