过滤器(Filter)与拦截器
一、过滤器(Filter)
1.概念
- 什么是过滤器
过滤器(Filter)是处于客户端与服务器目标资源之间的一道过滤技术。
- 过滤器作用
在访问目标资源文件之前,通过一系列的过滤器对请求进行修改、判断等,把不符合规则的请求在中途拦截或修改。也可以对响应进行过滤,拦截或修改响应。例如:实现权限访问控制、过滤敏感词汇、压缩响应信息功能、简单登录认证、ip黑名单封禁请求等。
2.过滤的过程
3.代码实现
存在注解与配置类实现两种方法,推荐配置类实现
- 简单的登录认证过滤
/**
* Copyright (c) bigbeardhk@163.com Corporation 2022. All Rights Reserved.
*/
package com.hk.study.config;
import com.hk.study.filter.LoginFilter;
import com.hk.study.filter.TestFilter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.HashMap;
import java.util.Map;
/**
* 过滤器配置类
*
* @Author : bigbeardhk
* @Date : 2022/05/03 15:51
**/
@Configuration
public class FilterConfig {
@Bean
public FilterRegistrationBean authFilterRegistation(){
FilterRegistrationBean registrationBean = new FilterRegistrationBean();
//注册bean
registrationBean.setFilter(new LoginFilter());
//设置bean name
registrationBean.setName("LoginFilter");
//设置属性值
Map<String, String> initParameters = new HashMap<>(2);
initParameters.put("includeUrls","/hello/login,/swagger-ui.html");
registrationBean.setInitParameters(initParameters);
//拦截所有请求
registrationBean.addUrlPatterns("/*");
//执行顺序,数字越小优先级越高
registrationBean.setOrder(1);
return registrationBean;
}
@Bean
public FilterRegistrationBean testFilterRegistation(){
System.out.println("=========过滤器配置Bean方法testFilterRegistation===========");
FilterRegistrationBean registrationBean = new FilterRegistrationBean();
//注册bean
registrationBean.setFilter(new TestFilter());
//设置bean name
registrationBean.setName("TestFilter");
//拦截所有请求
registrationBean.addUrlPatterns("/*");
//执行顺序,数字越小优先级越高
registrationBean.setOrder(9);
return registrationBean;
}
}
/**
* Copyright (c) bigbeardhk@163.com Corporation 2022. All Rights Reserved.
*/
package com.hk.study.filter;
import com.hk.study.util.StringUtil;
import lombok.extern.slf4j.Slf4j;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
/**
* 登录认证
*
* @Author : bigbeardhk
* @Date : 2022/05/03 15:35
**/
@Slf4j
public class LoginFilter implements Filter {
/**
* 不需要登录就可以访问的路径(比如:注册登录等)
*/
private String includeUrls;
/**
* 只在tomcat容器启动时执行一次
*
* @param filterConfig
* @throws ServletException
*/
@Override
public void init(FilterConfig filterConfig) throws ServletException {
//获取初始化filter的参数
// this.includeUrls = filterConfig.getInitParameter("includeUrls");
this.includeUrls = "/hello/login,/swagger-ui.html";
}
/**
* 客服端发生请求时doFilter前置执行,服务端返回请求时doFilter后置执行
* 模拟登录认证
* 1.拦截使用/*(但登录地址/hello/login放行)
* 2.所以到达/hello/login接口后,验证密码,
* 密码正确就会在服务器的session中进行setAttribute("user", name + pass)操作;
* 3.然后请求其它地址中,通过cooking中sessionId,我们在服务器中发现它对应的
* session.getAttribute("user")是存在的,说明他登录过,所以放行这次请求
*
* @param servletRequest
* @param servletResponse
* @param filterChain
* @throws IOException
* @throws ServletException
*/
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
HttpSession session = request.getSession();
String uri = request.getRequestURI();
log.info("filter url:"+uri);
//不需要过滤直接传给下一个过滤器
if (!StringUtil.isEmpty(includeUrls) && includeUrls.contains(uri)) {
filterChain.doFilter(servletRequest, servletResponse);
} else {
//需要过滤器
// session中包含user对象,则是登录状态
if(session!=null&&session.getAttribute("user") != null){
System.out.println("user:"+session.getAttribute("user"));
filterChain.doFilter(request, response);
}else{
response.setContentType("Application/json;charset=UTF-8");
response.getWriter().write("您还未登录");
// 重定向到登录页(需要在static文件夹下建立此html文件)
// response.sendRedirect(request.getContextPath()+"/user/login.html");
return;
}
}
}
@Override
public void destroy() {
}
}
二、拦截器(HandlerInterceptor)
执行顺序
代码实现
/**
* Copyright (c) bigbeardhk@163.com Corporation 2022. All Rights Reserved.
*/
package com.hk.study.intercept;
import lombok.extern.slf4j.Slf4j;
import org.springframework.lang.Nullable;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 日志拦截器实现
* @Author : bigbeardhk
* @Date : 2022/05/03 19:30
**/
@Slf4j
public class LogInterceptor implements HandlerInterceptor{
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
log.info("日志拦截器preHandle方法执行");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
log.info("日志拦截器postHandle方法执行");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
log.info("日志拦截器afterCompletion方法执行");
}
}
/**
* Copyright (c) bigbeardhk@163.com Corporation 2022. All Rights Reserved.
*/
package com.hk.study.config;
import com.hk.study.intercept.LogInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* 拦截器全局配置
*
* @Author : bigbeardhk
* @Date : 2022/05/03 19:32
**/
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
//需要拦截的路径,/**表示拦截所有请求
// String[] addPathPatterns={"/**"};
String[] addPathPatterns={"/sdgdfgf"};
//不需要拦截的路径
String[] excludePathPatterns={"/boot/login","/boot/exit"};
//新注册的过滤器
registry.addInterceptor(new LogInterceptor())
.addPathPatterns(addPathPatterns)
.excludePathPatterns(excludePathPatterns);
/*registry.addInterceptor(new AuthIntercepter())
.addPathPatterns(addPathPatterns)
.excludePathPatterns(excludePathPatterns);*/
}
}
三、切面编程(Aspect)
四、拦截器,过滤器,Aspect的区别
- 共同点
三者都是AOP思想体现
都可以对HttpServletRequest对象进行处理,日志、权限控制等
- 区别
Filter属于Servlet规范,Intercepter、Spring AOP属于Spring框架
实现AOP的方式不同,Filter用回调函数实现,一般情况下拿不到Spring bean对象,Intercepter用责任链实现,Spring AOP基于动态代理
- 执行顺序
五、常见问题
- 过滤器各方法的执行时机?
- init()与destroy()方法只会在tomcat(项目)启动或关闭时执行一次(一定会执行,不受配置URL影响),不会在客服端请求后再次执行
- doFilter()在客服端请求(根据配置判断是否拦截)时执行,request请求时,执行doFilter上面代码;response响应时,执行doFilter下面代码;
- 过滤器,拦截器的执行顺序?
资料学习与引用
- https://junshuai.blog.csdn.net/article/details/112061671?spm=1001.2101.3001.6650.5&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-5.pc_relevant_default&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-5.pc_relevant_default&utm_relevant_index=10
- https://blog.csdn.net/bubble21/article/details/102544638
- https://blog.csdn.net/qq1515312832/article/details/106871843