过滤器(filter)
目的
在 请求到达指定 url 前进行请求头的校验, 增删改以及校验请求头中的内容
链式结构, 会不断的往下一个 过滤器传递
使用方式
先创建一个自定义的 MyXXXFilter 实现 javax.servlet 包下面的 Filter 接口
- 自定义 MyEncodingFilter 过滤器
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* * 自定义过滤器, 设置请求编码类型
*
* @author yxg
* @date 2022/3/3 11:42
*/
@Slf4j
@Component
public class MyEncodingFilter implements Filter {
private FilterConfig filterConfig = null;
private String encoding = null;
@Override
public void init(FilterConfig filterConfig) throws ServletException {
this.filterConfig = filterConfig;
this.encoding = filterConfig.getInitParameter("encoding");
}
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
ServletContext context = this.filterConfig.getServletContext();
context.log("=====> 设置请求编码格式");
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
String encoding = getEncoding();
if (encoding == null) {
encoding = "gb2312";
}
request.setCharacterEncoding(encoding);
try {
log.info("设置请求编码格式 过滤器被调用了.........请求地址为: {}",request.getRequestURI());
chain.doFilter(request, response);
context.log("=====> 设置请求编码格式 成功");
} catch (Exception e) {
log.error("=====> 调用过滤链异常,异常信息为: {}", e.getMessage());
}
}
@Override
public void destroy() {
this.encoding = null;
}
public String getEncoding() {
return encoding;
}
}
- 在来一个 MyLogFilter 过滤器
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* *过滤器的作用就是在请求来的时候拦截所有请求, 对其请求头进行校验, 以及 重新定义请求头里面的内容
*
* @author yxg
* @date 2022/3/2 14:09
*/
@Slf4j
@Component
public class MyLogFilter implements Filter {
String[] excludedUrl;
@Override
public void init(FilterConfig filterConfig) throws ServletException {
excludedUrl = filterConfig.getInitParameter("excludedUrl").split(",");
}
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
log.info("过滤器被调用了........");
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
try {
String requestURI = request.getRequestURI();
if (isExcludedUrl(requestURI)) {
log.info("日志过滤器被调用了...是不需要处理的请求地址...请求地址为: {}", request.getRequestURI());
// 不进行后续的链的操作
return;
}
log.info("日志过滤器被调用了.........请求地址为: {}", request.getRequestURI());
log.info("日志过滤器被调用了.........请求ip为: {}", request.getRemoteHost());
log.info("日志过滤器被调用了.........请求端口port为: {}", request.getRemotePort());
chain.doFilter(request, response);
} catch (Exception e) {
log.error("=====> 调用过滤链异常,异常信息为: {}", e.getMessage());
}
}
private boolean isExcludedUrl(String url) {
if (excludedUrl == null || excludedUrl.length <= 0) {
return false;
}
for (String ex : excludedUrl) {
url = url.trim();
ex = ex.trim();
if (url.toLowerCase().matches(ex.toLowerCase().replace("*", ".*"))) {
return true;
}
}
return false;
}
@Override
public void destroy() {
}
}
- 最后创建一个 servlet 的配置类 WebConfiguration, 注意加上 @Configuration 注解
import com.nyist.myticket.web.filter.MyEncodingFilter;
import com.nyist.myticket.web.filter.MyLogFilter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* * 过滤器 配置
*
* @author yxg
* @date 2022/3/3 10:56
*/
@Configuration
public class WebConfiguration {
// 调用的顺序是 order 数值越小 , 越先执行
// registrationBean.setOrder(1);
@Bean
public FilterRegistrationBean encodingFilter() {
FilterRegistrationBean registrationBean = new FilterRegistrationBean<>();
// 将自定义的 filter 注册进 filter 注册表
registrationBean.setFilter(new MyEncodingFilter());
// 添加 类型,
// 过滤 应用程序中所有资源, 当前的应用程序根下的所有文件夹, * 前面要有 /
registrationBean.addUrlPatterns("/*");
// 过滤 指定类型的文件资源, 当前应用程序根目录下的所有 .html 文件, 注意 .html 前面没有 /
//registrationBean.addUrlPatterns("*.html");
// 过滤 指定目录下的所有文件, 当前应用程序根目录下的 controller 子目录下的所有文件
registrationBean.addUrlPatterns("/controller/*");
// 过滤指定文件
registrationBean.addUrlPatterns("/index.html");
// ...还可以有很多
StringBuilder excludedUrl = new StringBuilder();
//excludedUrl.append("/");
//excludedUrl.append("/");
registrationBean.addInitParameter("excludedUrl",excludedUrl.toString());
registrationBean.setName("encodingFilter");
// 数值越小 , 越先执行
registrationBean.setOrder(1);
return registrationBean;
}
@Bean
public FilterRegistrationBean loggerFilter() {
FilterRegistrationBean registrationBean = new FilterRegistrationBean<>();
// 将自定义的 filter 注册进 filter 注册表
registrationBean.setFilter(new MyLogFilter());
// 添加 类型,
// 过滤 应用程序中所有资源, 当前的应用程序根下的所有文件夹, * 前面要有 /
registrationBean.addUrlPatterns("/*");
// 过滤 指定类型的文件资源, 当前应用程序根目录下的所有 .html 文件, 注意 .html 前面没有 /
//registrationBean.addUrlPatterns(".html");
// 过滤 指定目录下的所有文件, 当前应用程序根目录下的 controller 子目录下的所有文件
registrationBean.addUrlPatterns("/controller/*");
// 过滤指定文件
registrationBean.addUrlPatterns("/index.html");
// ...还可以有很多
StringBuilder excludedUrl = new StringBuilder();
excludedUrl.append("/userInfo");
registrationBean.addInitParameter("excludedUrl","");
registrationBean.setName("loggerFilter");
// 数值越小, 级别越高
registrationBean.setOrder(2);
return registrationBean;
}
}
拦截器
目的在请求指定的方法或者字段之前或者之后进行拦截操作, 添加日志或者校验
使用方式
- 先创建一个 自定义 拦截器 MyInterceptor 实现 import org.springframework.web.servlet.HandlerInterceptor; 下的 HandlerInterceptor 接口
import com.nyist.myticket.core.factory.GenerateCodeFactory;
import com.nyist.myticket.core.utils.constant.SysConstant;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* *拦截器, 计算请求时间
*
* @author yxg
* @date 2022/3/2 16:28
*/
@Slf4j
@Component
public class MyInterceptor implements HandlerInterceptor {
public final static Logger logger = LoggerFactory.getLogger(MyInterceptor.class);
private ThreadLocal<Long> timeThreadLocal = new ThreadLocal<Long>();
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
logger.info("拦截器被调用了....preHandle");
logger.info("================================ start ================================");
String logTraceId = request.getHeader(SysConstant.LOG_TRACE_ID);
if (StringUtils.isBlank(logTraceId)) {
Long snowflakeId = GenerateCodeFactory.snowflakeId();
logTraceId = snowflakeId.toString();
}
MDC.put(SysConstant.LOG_TRACE_ID, logTraceId);
long beginTime = System.currentTimeMillis(); // 1、开始时间
timeThreadLocal.set(beginTime); // 线程绑定变量(该数据只有当前请求的线程可见)
return true; // 继续流程
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) {
logger.info("拦截器被调用了....postHandle");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
logger.info("拦截器被调用了....afterCompletion");
long endTime = System.currentTimeMillis();// 2、结束时间
long beginTime = timeThreadLocal.get();// 得到线程绑定的局部变量(开始时间)
String requestURI = request.getRequestURI();
long requestTime = endTime - beginTime;
if (requestTime > 500) {// 此处认为处理时间超过500毫秒的请求为慢请求
logger.warn(requestURI + " 比较耗时:" + requestTime);
} else {
logger.info("=====>请求地址为:" + requestURI + " 耗时为: {}", requestTime);
}
MDC.remove(SysConstant.LOG_TRACE_ID);
logger.info("================================ end ================================");
}
}
- 在创建一个 mvc 的配置类 WebMvcConfig 也要加 @Configuration 注解
import com.nyist.myticket.web.interceptor.MyInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* * 拦截器配置类
*
* @author yxg
* @date 2022/3/2 17:03
*/
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Autowired
private MyInterceptor myInterceptor;
/**
* 添加 拦截器注册表
* @author yxg
* @date 2022/3/3 9:41
* @param registry
* @return void
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(myInterceptor) // 添加自定义拦截器
.addPathPatterns("/**"); // 添加拦截路径
}
}
这样在 Springboot 启动的时候就会为我们配置好我们自己的 过滤器和拦截器
他们的请求流程如下图:
切面(Aspect) 明天继续研究
参考文章1-基础代码 = https://www.yht7.com/news/154190
参考文章2-多个过滤器配置 = https://blog.csdn.net/WoddenFish/article/details/84836824
参考文章3-图片来源 = https://www.cnblogs.com/panxuejun/p/7715917.html
参考文章4-拦截器 = https://zhuanlan.zhihu.com/p/408809649
参考文章5-区别 = https://blog.csdn.net/weixin_44502804/article/details/93139550
参考文章6-最后一张图来源 = https://developer.aliyun.com/article/636629