第一篇-基础-第六章-过滤器、拦截器与监听器

项目中经常需要对HTTP请求进行拦截和处理,以实现诸如身份验证、授权、日志记录等功能,SpringBoot提供了过滤器和拦截器,此外,对实现网站访问量、记录用户访问路径、系统启动时加载初始化信息等功能能,SpringBoot提供了监听器。

6.1-过滤器

6.1.1 构建过滤器

过滤器用于对数据进行过滤处理,通过过滤器可以对用户通过RUL地址发送的请求进行过滤处理(过滤一些错误的请求/请求的敏感词),可以对服务器返回的数据进行过滤处理(压缩响应信息,甚至信息加密等)

要构建一个过滤器,只要这个类实现Filter接口即可,就能实现自定义的过滤器。Filter是Servlet下的包。

public class LoginFilter implements Filter {

    //过滤器初始化时会调用的方法,默认方法,可以不重写
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    //抽象方法,必须重写,过滤器核心方法,实现过滤业务
    //chain为过滤链对象,也有doFilter()方法,主要表示放过,传给下一个过滤器
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest) request;
        Object user = req.getSession().getAttribute("user");
        if (user==null){//没登录哟,跳去登录页面
            req.getRequestDispatcher("/user/login").forward(request,response);
        }else{
            chain.doFilter(request,response);
        }

    }

    //过滤器销毁时会调用的方法,默认方法,可以不重写
    @Override
    public void destroy() {

    }
}

6.1.2 配置过滤器

构建过滤器用的是servlet本身的功能,SpringBoot是不知道这回事的,所以需要配置过滤器,绑定到SpringBoot容器中。

6.1.2.1通过FilterRegistrationBean类配置过滤器

FilterRegistrationBean是SpringBoot提供的专门用于配置过滤器的类。FilterRegistrationBean类是一个泛型类,即FilterRegistrationBean,T表示过滤器类型,因为一个项目可能配置多个过滤器,所以使用不同的泛型表示不同类型的过滤器,可以有效防止这些过滤器发生冲突。

FilterRegistrationBean类的常用方法如下:

返回值

方法

说明

void

addUrlPatterns(String...)

设置过滤的路径

void

setName(String)

设置过滤器名字

void

setEnabled(boolean)

设置是否启用过滤器,默认true

boolean

isEnabled()

此过滤器是否启用

void

setFilter(T filter)

设置要配置的过滤器对象

T

getFilter()

获取已配置的过滤器对象

void

setOrder(int)

设置过滤器的优先级,值越小优先级越高,1为最高,默认为最小

int

getOrder()

获取过滤器优先级

以配置判断登录的过滤器为例

@Configuration
public class FilterConfig {

    @Bean
    public FilterRegistrationBean getFilter() {
        FilterRegistrationBean bean = new FilterRegistrationBean<>();
        bean.setFilter(new LoginFilter());//把登录过滤器设置进去
        bean.addUrlPatterns("/main/*");//过滤下的所有子路径,可以设置多个,别把通配符给漏了
        bean.setName("loginFilter");
        bean.setOrder(1);//1为最高,默认为21亿多
        return bean;
    }
}
6.1.2.2通过@WebFilter注解配置过滤器

@WebFilter注解可用于快速配置过滤器,只不过@WebFilter注解没有FilterRegistrationBean类的功能多(也没多几个),使用@WebFilter注解标注的类必须使用@Componnet注解予以标注,否则就无法扫描到(FilterRegistrationBean要是在类上没@Configuration也等于白写),@WebFilter直接作用在过滤器上。

@WebFilter的value属性等同于FilterRegistrationBean的urlPattern属性功能

对上面的登录拦截做修改

@Component
@WebFilter("/main/*")
public class LoginFilter implements Filter {

    //过滤器初始化时会调用的方法,默认方法,可以不重写
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    //抽象方法,必须重写,过滤器核心方法,实现过滤业务
    //chain为过滤链对象,也有doFilter()方法,主要表示放过,传给下一个过滤器
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest) request;
        Object user = req.getSession().getAttribute("user");
        if (user==null){//没登录哟,跳去登录页面
            req.getRequestDispatcher("/user/login").forward(request,response);
        }else{
            chain.doFilter(request,response);
        }

    }

    //过滤器销毁时会调用的方法,默认方法,可以不重写
    @Override
    public void destroy() {

    }
}

感觉@WebFilter比FilterRegistrationBean方便太多,毕竟2行代码和写一个类孰轻孰重大家都有感觉。

书中网站访问量统计的过滤器如下:

1,使用@WebFilter标注访问过滤器,并对过滤器初始化和访问进行标记

@Component
@WebFilter("*/*")//对所有的访问进行过滤
public class VisitCountFilter implements Filter {

    public static String VISIT_COUNT = "visit_count";

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        ServletContext context = filterConfig.getServletContext();//获取过滤器上下文
        context.setAttribute(VISIT_COUNT, 0);               //设置访问初始化为0
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest) request;
        ServletContext context = req.getServletContext();
        Integer count = (Integer) context.getAttribute(VISIT_COUNT);
        context.setAttribute(VISIT_COUNT, ++count);
        filterChain.doFilter(request, response);
    }
}

2,在控制器类进行访问量提取

@GetMapping("/visit")
public String visitCount(HttpServletRequest httpServletRequest) {
    ServletContext context = httpServletRequest.getServletContext();
    Integer count= (Integer) context.getAttribute(VisitCountFilter.VISIT_COUNT);
    return "当前访问量=" + count;
}

6.2-拦截器

SpringBoot的拦截器用于拦截用户请求并做相应的处理,比如验证用户是否登录、日志记录、权限管理等。

6.2.1拦截器概述

拦截器可以在控制器类中的方法被执行前和被执行后对请求做一些处理,因此,拦截器可以安装设定条件中断请求,多个拦截器可以组成一个拦截链,链中任何一个拦截器都能中断请求,同事整个拦截链也会中断。

拦截器和过滤器有点像,都可以去请求做一定的处理,他们最大的区别在于触发的时机不同。

  1. 拦截器可以在控制器类中的的方法被执行前和执行后对请求做一些处理
  2. 过滤器对用户通过URL地址发送的请求进行预处理,对服务器返回的数据进行后处理。

在开发中,一个类如果实现了HandlerInterceptor接口,就能实现自定义的拦截器,主要是重写实现3个方法.

/**
 * 拦截器
 */
public class MyInterCeptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        return false;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {

    }
}

1,preHandle()方法: 会在控制器类中方法被执行之前、对请求进行处理时被执行。在此方法中,request和response分别表示请求对象和响应对象,handler表示请求的处理程序对象,在通常情况下,handler对象是用于封装方法的HandlerMethod对象,因此,可以通过handler对象的类型知晓请求将进入控制器类中的哪个方法。极个别情况下,handler对象也有可能是处理静态资源的ResourceHttpRequestHandler对象,因此在转换handler对象类型之前需要现对handler对象的类型进行判断。

preHandle()方法返回值是一个布尔值,如果返回true,则表示请求通过,控制器类中的方法可以继续执行,如果为false,则请求中断,方法不执行。

2,postHandle()方法: 会在控制器类中的方法被执行之后、对请求进行处理时被执行。

3,afterCompletion()方法: 在控制器方法执行完之后执行。

同样的,创建拦截器后也需要进行配置,配置类使用@Configuration进行标注,同时实现WebMvcConfigurer接口,并重写addInterceptors(),添加拦截器和添加需要拦截的url即可。

6.2.2自定义拦截器并配置

实现一个拦截器,用于捕捉一个请求一次被preHandle()、postHandle()和afterCompletion()方法处理的事先。

具体操作为创建MyInterceptor拦截器类,实现HandlerInterceptor接口,在preHandle()方法中输入要请求的方法,并读取请求中value参数的值;在控制器类处理完请求后利用拦截器查看请求中value参数是否发生变化。

1,创建自定义拦截器

/**
 * 拦截器
 */
public class MyInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if (handler instanceof HandlerMethod) {//判断handler类型
            HandlerMethod method = (HandlerMethod) handler;
            System.out.println("preHandle 请求访问的方法=" + method.getMethod().getName() + "()");
            Object value = request.getAttribute("value");
            System.out.println("preHandle 执行方法前value=" + value);
            return true;
        }
        return false;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        Object value=request.getAttribute("value");//执行完请求,在读取此属性
        System.out.println("postHandle value="+value);
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        request.removeAttribute("value");//请求执行完毕,释放工作
        System.out.println("afterCompletion");
    }
}

2,在配置类中配置拦截器

@Configuration
public class MyInterceptorConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        InterceptorRegistration regist = registry.addInterceptor(new MyInterceptor());//添加拦截器
        regist.addPathPatterns("/**");//拦截所有地址
    }
}

3,在控制器类中添加方法

@RequestMapping("/main/hello")
public String hello(HttpServletRequest request) {
    request.setAttribute("value","我是方法中写入的value");
    System.out.println("我在执行请求方法");
    return "hello springboot" + env.getProperty("new_name");
}

4,显示日志如下

preHandle 请求访问的方法=hello()
preHandle 执行方法前value=null
我在执行请求方法
postHandle value=我是方法中写入的value
afterCompletion

看到拦截器起作用了,preHandle()确实在访问请求前执行了,postHandle()在执行请求方法后执行了,并获取到了请求中写入的信息,afterCompletion()执行在最后面,确实起到了拦截的作用。

6.3-监听器

监听器用于监听并完成指定的事件。

6.3.1监听器概述

监听器不像过滤器和拦截器那样,创建完还需要配置,开发人员自定义的监听器类只要实现特定的监听器接口并用@Component注解予以标注即可生效,虽然不需要配置,但监听器有8个可以实现的监听接口。

6.3.1.1-ServletRequestListener

ServletRequestListener接口可以监听请求的初始化和销毁

@Component
public class MyServletRequestListener implements ServletRequestListener {
    @Override
    public void requestInitialized(ServletRequestEvent sre) {
        //请求初始化时出发
    }

    @Override
    public void requestDestroyed(ServletRequestEvent sre) {
        //请求被销毁是出发
    }
}
6.3.1.2-HttpSessionListener接口

HttpSessionListener接口可以监听session的创建和销毁

@Component
public class MyHttpSessionListener implements HttpSessionListener {

    @Override
    public void sessionCreated(HttpSessionEvent se) {
        //session已经被夹在及初始化触发
    }

    @Override
    public void sessionDestroyed(HttpSessionEvent se) {
        //session被销毁后触发
    }
}
6.3.1.3-ServletContextListener接口

ServletContextListener接口可以监听上下文的初始化和销毁

@Component
public class MyServletContextListener implements ServletContextListener {
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        //上下文初始化时触发
    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        //上下文被销毁时触发
    }
}
6.3.1.4-ServletRequestAttributeListener接口

ServletRequestAttributeListener接口可以监听请求属性发生的增、删、改事件

@Component
public class MyServletRequestAttributeListener implements ServletRequestAttributeListener {
    @Override
    public void attributeAdded(ServletRequestAttributeEvent srae) {
        //请求添加新属性时触发
    }

    @Override
    public void attributeRemoved(ServletRequestAttributeEvent srae) {
        //请求删除旧属性时触发
    }

    @Override
    public void attributeReplaced(ServletRequestAttributeEvent srae) {
        //请求修改旧属性时触发
    }
}
6.3.1.5-HttpSessionAttributeListener接口

HttpSessionAttributeListener接口可以监听session属性发生增、删、改时间

@Component
public class MyHttpSessionAttributeListener implements HttpSessionAttributeListener {

    @Override
    public void attributeAdded(HttpSessionBindingEvent se) {
        //session添加新属性时触发
    }

    @Override
    public void attributeRemoved(HttpSessionBindingEvent se) {
        //session删除旧属性时触发
    }

    @Override
    public void attributeReplaced(HttpSessionBindingEvent se) {
        //session修改旧属性时触发
    }
}
6.3.1.6-ServletContextAttributeListener接口

ServletContextAttributeListener接口可以监听上下文属性发生的增、删和改事件

@Component
public class MyServletContextAttributeListener implements ServletContextAttributeListener {
    @Override
    public void attributeAdded(ServletContextAttributeEvent scae) {
        //上下文添加新属性时触发
    }

    @Override
    public void attributeRemoved(ServletContextAttributeEvent scae) {
        //上下文删除旧属性时触发
    }

    @Override
    public void attributeReplaced(ServletContextAttributeEvent scae) {
        //上下文修改旧属性时触发
    }
}
6.3.1.7-HttpSessionBindingListener接口

HttpSessionBindingListener接口可以为开发者自定义的类添加session绑定监听,当session保存或者移除此类的对象时触发此监听

@Component
public class MyHttpSessionBindingListener implements HttpSessionBindingListener {
    @Override
    public void valueBound(HttpSessionBindingEvent event) {
        //当session通过setAttribute()方法保存对象时可以触发此方法
    }

    @Override
    public void valueUnbound(HttpSessionBindingEvent event) {
        //当session通过removeAttribute()方法移除对象时可以触发此方法
    }
}
6.3.1.8-HttpSessionActivationListener接口

HttpSessionActivationListener接口可以给开发者自定义的类添加序列化监听,当保存在session中的自定义类对象被序列化或反序列化时触发监听。

@Component
public class MyHttpSessionActivationListener implements HttpSessionActivationListener {

    @Override
    public void sessionWillPassivate(HttpSessionEvent se) {
        //自定义对象被序列化之前触发
    }

    @Override
    public void sessionDidActivate(HttpSessionEvent se) {
        //自定义对象被反序列化之后触发
    }
}

6.3.2-自定义监听器

创建一个常用的监听器。网站必须记录每一个请求的来源和行为。

服务器可以通过监听器在请求创建时记录请求的信息-包括请求的IP、session id和请求访问的URL地址等信息。

session id是由服务器随机生成的,每个session的session id都是不一样的,只要用户不关闭浏览器,用户的session id就不会变。

具体思路为实现ServletRequestListener接口。

@Component
public class MyCustomListener implements ServletRequestListener {

    @Override
    public void requestInitialized(ServletRequestEvent sre) {
        HttpServletRequest request = (HttpServletRequest) sre.getServletRequest();
        String ip = request.getRemoteAddr();//请求的ip
        String url = request.getRequestURL().toString();//请求访问的地址
        String sessionId = request.getSession().getId();//获取session Id
        System.out.println("ip=" + ip);
        System.out.println("url=" + url);
        System.out.println("sessionId=" + sessionId);
    }

    @Override
    public void requestDestroyed(ServletRequestEvent sre) {
        HttpServletRequest request = (HttpServletRequest) sre.getServletRequest();
        String session_id = request.getSession().getId();
        System.out.println("session id=" + session_id + "已被销毁");
    }
}

打完收工。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Mr_Sun_01

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值