1、过滤器Filter
作用是防止SQL注入、参数过滤、防止页面攻击、空参数矫正、Token校验、Session验证、点击率统计等等;
使用Filter的步骤
- 新建类,实现Filter抽象类;
- 重写init、doFilter、destroy方法;
- 在SpringBoot入口中添加注解@ServletComponentScan,以注册Filter;
注意:通过@Order注解设置过滤器的执行顺序,越小的越先被执行;
import org.springframework.core.annotation.Order;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
/**
* @ClassName: FilterDemo
* @Author: 中都
* @Date: 2021/12/29 22:11
* @Description: 过滤器
*/
@Order(1)
@WebFilter(filterName = "FilterDemo",urlPatterns = "/*")
public class FilterDemo implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("初始化逻辑,服务器启动时调用");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("拦截器");
filterChain.doFilter(servletRequest,servletResponse);
}
@Override
public void destroy() {
System.out.println("销毁逻辑,服务器关闭时调用");
}
}
@ServletComponentScan
@SpringBootApplication
public class SpringbootstudyApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootstudyApplication.class, args);
}
}
示例代码:
/**
* @Author: zhondu
* @Date: 2023/1/25 18:36
* @Desc: 过滤器
*/
@Component
public class LogFilter implements Filter {
public static final Logger logger = LoggerFactory.getLogger(LogFilter.class);
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
// filterChain是过滤器执行链,因为可以有多个过滤器的
// 打印请求信息
HttpServletRequest request = (HttpServletRequest) servletRequest;
logger.info("-----------LogFilter 开始--------------");
// 接口和方法类型(get post)
logger.info("请求地址:{} {}", request.getRequestURL().toString(), request.getMethod());
logger.info("远程地址:{}", request.getRemoteAddr());
// 还可以打印其他很多信息,请求头之类的,因为这里拿到的是整个request
long startTime = System.currentTimeMillis();
filterChain.doFilter(servletRequest, servletResponse);
logger.info("-----------LogFilter 结束 耗时:{} ms--------------", System.currentTimeMillis() - startTime);
}
}
** 执行结果 **
2、监听器
监听对象的增删改查等操作,然后做出相应处理,用户统计在线人数、在线用户、系统加载时的信息初始化等等;
/**
* @Author: zhondu
* @Date: 2023/1/26 10:18
* @Desc: 拦截器 Spring特有的,常用于登录校验 权限校验 请求日志打印
*/
@Component
public class LogInterceptor implements HandlerInterceptor {
public static final Logger logger = LoggerFactory.getLogger(LogInterceptor.class);
/**
* 拦截器 -- 请求进入方法之前的拦截处理
* @param request
* @param response
* @param handler
* @return
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
logger.info("-----------LogInterceptor 开始--------------");
// 接口和方法类型(get post)
logger.info("请求地址:{} {}", request.getRequestURL().toString(), request.getMethod());
logger.info("远程地址:{}", request.getRemoteAddr());
long startTime = System.currentTimeMillis();
request.setAttribute("requestStartTime",startTime);
// 返回true 才会继续走,否则就直接结束了,所以可以用于 登录校验 权限校验
return true;
}
/**
* 拦截器 -- 走完方法之后返回前端的拦截处理
* @param request
* @param response
* @param handler
* @param modelAndView
* @throws Exception
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
long startTime = (Long) request.getAttribute("requestStartTime");
logger.info("-----------LogInterceptor 结束 耗时:{} ms--------------", System.currentTimeMillis() - startTime);
}
}
拦截器还需要加一个全局配置的:
import com.zhondu.wiki.interceptor.LogInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import javax.annotation.Resource;
/**
* @Author: zhondu
* @Date: 2023/1/26 10:26
* @Desc:
*/
@Configuration
public class SpringMvcConfig implements WebMvcConfigurer {
@Resource
private LogInterceptor logInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 增加过滤器 -- 针对所有的请求 (但是例如登录校验就不是所有的接口都需要校验,例如登录校验是不能校验登录接口本身的)
registry.addInterceptor(logInterceptor)
.addPathPatterns("/**") // 针对所有的请求
.excludePathPatterns("/login"); // 排除掉登录请求-不拦截
}
}
拦截器分为前后两个方法,过滤器只有一个,而且过滤器是容器级别的(Tomcat netty等),拦截器是应用级别的,例如web应用
3、AOP
AOP也可以打印参数相关的信息,但是与过滤器拦截器不同,他依靠的不是request,而是连接点JoinPoint,
需要引入依赖:
<!--AOP-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.70</version>
</dependency>
package com.zhondu.wiki.aspect;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.support.spring.PropertyPreFilters;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
/**
* @Author: zhondu
* @Date: 2023/1/26 11:49
* @Desc: AOP
*/
@Aspect
@Component
public class LogAspect {
public static final Logger logger = LoggerFactory.getLogger(LogAspect.class);
// 定义一个切点 监控所有的controller
@Pointcut("execution(public * com.zhondu.*.controller..*Controller.*(..))")
public void controllerPointcut() {}
// 前置通知
@Before("controllerPointcut()")
public void doBefore(JoinPoint joinPoint) throws Throwable {
// 开始打印请求日志
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
Signature signature = joinPoint.getSignature();
String name = signature.getName();
// 打印请求信息
logger.info("------------- 开始 -------------");
logger.info("请求地址: {} {}", request.getRequestURL().toString(), request.getMethod());
logger.info("类名方法: {}.{}", signature.getDeclaringTypeName(), name);
logger.info("远程地址: {}", request.getRemoteAddr());
// 打印请求参数
Object[] args = joinPoint.getArgs();
// LOG.info("请求参数: {}", JSONObject.toJSONString(args));
Object[] arguments = new Object[args.length];
for (int i = 0; i < args.length; i++) {
if (args[i] instanceof ServletRequest
|| args[i] instanceof ServletResponse
|| args[i] instanceof MultipartFile) {
continue;
}
arguments[i] = args[i];
}
// 排除字段,敏感字段或太长的字段不显示
String[] excludeProperties = {"password", "file"};
PropertyPreFilters filters = new PropertyPreFilters();
PropertyPreFilters.MySimplePropertyPreFilter excludefilter = filters.addFilter();
excludefilter.addExcludes(excludeProperties);
logger.info("请求参数: {}", JSONObject.toJSONString(arguments, excludefilter));
}
/**
* 环绕通知
* @param proceedingJoinPoint
* @return
* @throws Throwable
*/
@Around("controllerPointcut()")
public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
long startTime = System.currentTimeMillis();
Object result = proceedingJoinPoint.proceed();
// 排除字段,敏感字段或太长的字段不显示
String[] excludeProperties = {"password", "file"};
PropertyPreFilters filters = new PropertyPreFilters();
PropertyPreFilters.MySimplePropertyPreFilter excludefilter = filters.addFilter();
excludefilter.addExcludes(excludeProperties);
logger.info("返回结果: {}", JSONObject.toJSONString(result, excludefilter));
logger.info("------------- 结束 耗时:{} ms -------------", System.currentTimeMillis() - startTime);
return result;
}
}
### 区别: