Filter过滤器
前言
问:我们为什么要使用过滤器?
答:在web请求过程,过滤一些敏感词汇、验证Header请求头、防止XSS、SQL注入,能快速返回信息
问:过滤器是什么?
答:Filter是Servlet的一个组件在 请求到达 Servlet 之前,被一个或多个Filter处理。
请求过程
具体代码
自定义Filter
package com.nijo.example.web.filter;
import javax.servlet.*;
import javax.servlet.FilterConfig;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.Objects;
/**
* 自定义Filter过滤器
* 可以用作请求前 信息头验证(没有token、或自定义头)、拦截xss、SQL注入
* 这里提供2中方式注入 1.@webFilter() + @ServletComponentScan
* 2.Bean java config模式
*/
public class CustomFilter implements Filter {
/**
* 项目启动时候值会运行一次
* 可以用作当前数据初始化
* eg init add List<string>
*/
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("CustomFilter 初始化init()");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
//可以做信息头的检测、xss检测、sql注入、记录请求
System.out.println("start check RequestHeard.....");
String token = ((HttpServletRequest) servletRequest).getHeader("token");
if(Objects.isNull(token)||"".equals(token)){
//参数有误 转发请求到参数合法直接返回
servletRequest.getRequestDispatcher("/parameterException").forward(servletRequest,servletResponse);
}
else {
//符合条件 请求目标方法
filterChain.doFilter(servletRequest,servletResponse);
}
System.out.println("返回response 过滤参数");
}
/**
* 项目停止会调用该方法
* */
@Override
public void destroy() {
System.out.println("CustomFilter 销毁 destroy()");
}
}
JAVA config 方式配置 上诉讲了@webFilter注入容器
package com.nijo.example.web.filter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
@Component
public class FilterConfig {
@Bean
public FilterRegistrationBean filterRegistrationBean(){
FilterRegistrationBean registrationBean = new FilterRegistrationBean();
//自定义过滤器
registrationBean.setFilter(new CustomFilter());
//过滤/*所有路径
registrationBean.addUrlPatterns("/*");
//初始化数据 可以在dofilter()逻辑过滤不判断url
//registrationBean.addInitParameter("noFilter","/1,/2");
registrationBean.setName("customFilter");
//存在多个过滤器 设置优先级
registrationBean.setOrder(0);
return registrationBean;
}
}
测试 用例:http://127.0.0.1:8080/test
package com.nijo.example.web.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.Map;
@RestController
public class UserController {
@GetMapping("/parameterException")
Map<String,String> parameterException(){
Map<String,String> result=new HashMap<>();
result.put("200","参数异常");
return result;
}
@GetMapping("/test")
Map<String,String> test(){
Map<String,String> result=new HashMap<>();
result.put("200","通许success");
return result;
}
}
结果
Interceptor
前言
SpringWebMVC 拦截器,跟Servlet过滤器有点相识,主要请求进入Controller之前进行一些前置判断,所谓前置判断(用户登陆状态、请求性能记录等等)。如果知道MVC请求流程,这个拦截器知识就很easy。
主要方法
方法 | 描述 |
---|---|
boolean preHandle() | 请求会先执行这个预处理方法,可以实现登陆状态、验权逻辑 返回true 进入下一个Interceptor false中断 不进入Controller |
void postHandle() | 后置处理方法,在返回ModelAndView之前倒序执行每个Interceptor postHandle |
void afterCompletion() | 请求完毕,即视图渲染完成回调方法 可以做一些请求时间结束记录 |
与Filter区别
1.Filter属于Servlet组件,Interceptor输入webMvc组件
2.执行流程不同,先执行Filter 在执行Interceptor
3.web加载顺序是listener–>filter–>servlet–>Spring Filter过滤器中@Autowired是会空指针异常,解决办法 在过滤器init()
//init()初始化filter时手动注入bean对象
ServletContext context = filterConfig.getServletContext();
ApplicationContext ac = WebApplicationContextUtils .getWebApplicationContext(context);
请求过程
1.请求会先到Interceptor下preHandle 方法,返回true 进入下一个Interceptor 可以设置Order 请求顺序 ,直到所有拦截器都返回true,请求到目标方法,中途任意一个拦截器返回false 后面拦截器都不执行。
2.Controller 返回ModelAndView前 会倒序执行Interceptor下post Handle方法,所谓倒序就是拦截器2–>拦截器1 方式依次执行
3.在视图解析器解析完成后,会回调Interceptor下afterCompletion方法,也是倒序执行
源码
DispatcherServlet类 doDispatch方法
try {
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// Determine handler for the current request.
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
// Determine handler adapter for the current request.
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Process last-modified header, if supported by the handler.
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
//拦截器预处理preHandle
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
//调用具体Controller
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
applyDefaultViewName(processedRequest, mv);
//处理PostHandle 方法
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {
// As of 4.3, we're processing Errors thrown from handler methods as well,
// making them available for @ExceptionHandler methods and other scenarios.
dispatchException = new NestedServletException("Handler dispatch failed", err);
}
//调用afterCompletion
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
applyPreHandle 方法
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
for (int i = 0; i < this.interceptorList.size(); i++) {
HandlerInterceptor interceptor = this.interceptorList.get(i);
if (!interceptor.preHandle(request, response, this.handler)) {
//失败时触发afterCompletion的调用
triggerAfterCompletion(request, response, null);
return false;
}
this.interceptorIndex = i;
}
return true;
}
实现方式
1.直接使用实现HandlerInterceptor接口 此方式必须重写接口内所有方法
2.继承 HandlerInterceptorAdapter 已经过时,此方式不用重写所有方法,用到什么方法重写即可。底层还是继承HandlerInterceptor接口,解决方法创建一个类 实现HandlerInterceptor
自定义拦截器
/**
* 自定义拦截器
* 用于请求性能检测
* 继承 HandlerInterceptorAdapter 过时
* */
public class CoustomInterceptor implements HandlerInterceptor {
private ThreadLocal<Long> map = new ThreadLocal<>();
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
map.set(System.currentTimeMillis());
System.out.println("记录请求URL:" + request.getRequestURI() + "开始");
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
//结束请求时间
Long end = System.currentTimeMillis() - (map.get());
System.out.println("请求URL:"+request.getRequestURI()+",耗时:"+end+"ms");
map.remove();
}
}
java config方式配置将拦截器加入Spring
这里有两种方式
1.实现WebMvcConfigurer
2.继承WebMvcConfigurationSupport
/**
* 配置拦截器加入Spring
*/
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new CoustomInterceptor())
.addPathPatterns("/*");//拦截路径
WebMvcConfigurer.super.addInterceptors(registry);
}
// @Override
// protected void addInterceptors(InterceptorRegistry registry) {
// registry.addInterceptor(new CoustomInterceptor())
// .addPathPatterns("/*");//拦截路径
// super.addInterceptors(registry);
// }
}
结果
源码
Spring-Boot-Examples 目录:filter-Interceptor