中都

风习袅袅,盈水展千华,飞檐亭角清铃响。犹记当初,你回眸莞尔,一笑倾城百日香。

博客园 首页 新随笔 联系 订阅 管理

1、过滤器Filter

作用是防止SQL注入、参数过滤、防止页面攻击、空参数矫正、Token校验、Session验证、点击率统计等等;

使用Filter的步骤

  1. 新建类,实现Filter抽象类;
  2. 重写init、doFilter、destroy方法;
  3. 在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);
	}

}

image
image

示例代码:

/**
 * @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);
    }
}

** 执行结果 **
image

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"); // 排除掉登录请求-不拦截
    }
}

image

拦截器分为前后两个方法,过滤器只有一个,而且过滤器是容器级别的(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;
    }
}

image

区别:

image

posted on 2021-12-29 22:30  中都  阅读(104)  评论(0)    收藏  举报
Live2D