Springboot:过滤器与拦截器的使用

过滤器Filter

首先创建过滤器类,继承Filter。例如创建一个记录http请求和响应日志的过滤器

package edu.duan.springboot.http;

import edu.duan.springboot.util.ApiLogUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Order(Ordered.HIGHEST_PRECEDENCE)
// 添加@WebFilter注解,就不用FilterRegistrationBean配置
@WebFilter(urlPatterns = "/*", filterName = "LogFilter")
@Slf4j
public class LogFilter implements Filter {
    private final String CLASS_NAME = LogFilter.class.getSimpleName();

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        log.info("===========================过滤器开始工作===========================");
        BodyCachingHttpServletRequestWrapper requestWrapper = new BodyCachingHttpServletRequestWrapper((HttpServletRequest) servletRequest);
        BodyCachingHttpServletResponseWrapper responseWrapper = new BodyCachingHttpServletResponseWrapper((HttpServletResponse) servletResponse);
        ApiLogUtil.logRequest(requestWrapper);
        filterChain.doFilter(requestWrapper, responseWrapper);
        ApiLogUtil.logResponse(responseWrapper);
    }

    @Override
    public void destroy() {

    }
}

中间BodyCachingHttpServletRequestWrapper和 BodyCachingHttpServletResponseWrapper 是刻意包装的,目的是多次读取http请求中的数据(spring原生的http请求类只允许读取一次数据);以及ApiLogUtil是打印日志的工具类。详见附录

要使过滤器生效,有两种办法。

Ⅰ:添加spring配置类,实现@bean注入

package edu.duan.springboot.config;


import edu.duan.springboot.http.LogFilter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * 过滤器配置
 * 若添加@ServletComponentScan和@WebFilter注解,就不用当前配置类
 */
@Configuration
public class FilterConfig {

    @Bean
    public FilterRegistrationBean registFilter() {
        FilterRegistrationBean registration = new FilterRegistrationBean();
        registration.setFilter(new LogFilter());
        registration.addUrlPatterns("/*");
        registration.setName("LogFilter");
        registration.setOrder(1);
        return registration;
    }

}

Ⅱ:通过注解

在Filter类添加注解@Order(多个过滤器时决定执行顺序,单个时可省略),@WebFilter

@Order(Ordered.HIGHEST_PRECEDENCE)
@WebFilter(urlPatterns = "/*", filterName = "LogFilter")

并在启动类添加注解@ServletComponentScan,目的是扫描@WebFilter注解的类

@ServletComponentScan
@SpringBootApplication
public class Application implements CommandLineRunner

若@WebFilter所在类不在Application 启动类的所在包或子包中,则@ServletComponentScan需要写明路径

两种方法功能重叠,取其一即可

实现的效果:

2020-01-05 16:05:20.867  INFO 15448 --- [nio-8080-exec-1] edu.duan.springboot.http.LogFilter       : ===========================过滤器开始工作===========================
2020-01-05 16:05:20.953  INFO 15448 --- [nio-8080-exec-1] edu.duan.springboot.util.ApiLogUtil      : 请求:127.0.0.1,HTTP/1.1, POST, http://localhost:8080/api/user, headers={"content-length":"9","host":"localhost:8080","content-type":"application/json;charset=UTF-8","connection":"Keep-Alive","cache-control":"no-cache","accept-encoding":"gzip,deflate","accept":"*/*","user-agent":"Apache-HttpClient/4.5.2 (Java/1.8.0_152-release)"}, params={}, body={"uid":1}
2020-01-05 16:05:22.007  INFO 15448 --- [nio-8080-exec-1] edu.duan.springboot.util.ApiLogUtil      : 响应:response={"code":200,"msg":null,"body":[{"uid":1,"uname":"张三","uage":15}]}

拦截器Interceptor

创建拦截器类,继承HandlerInterceptor。创建一个与上述过滤器同样功能的拦截器

package edu.duan.springboot.http;

import edu.duan.springboot.util.ApiLogUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Slf4j
public class LogInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
        log.info("===========================拦截器开始任务===========================");
        BodyCachingHttpServletRequestWrapper requestWrapper = (BodyCachingHttpServletRequestWrapper) httpServletRequest;
        ApiLogUtil.logRequest(requestWrapper);
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {

    }

    @Override
    public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
        // 只能强制类型转换,不能new一个新对象!
        BodyCachingHttpServletResponseWrapper responseWrapper = (BodyCachingHttpServletResponseWrapper) httpServletResponse;
        ApiLogUtil.logResponse(responseWrapper);
        log.info("===========================拦截器本次任务结束===========================");
    }
}

增加配置文件使拦截器生效

package edu.duan.springboot.config;

import edu.duan.springboot.http.LogInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

/**
 * 拦截器配置
 */
@Configuration
public class InterceptorConfig extends WebMvcConfigurerAdapter {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LogInterceptor()).addPathPatterns("/**");
        super.addInterceptors(registry);
    }
}

实现的效果:

2020-01-05 16:11:03.765  INFO 7796 --- [nio-8080-exec-1] edu.duan.springboot.http.LogFilter       : ===========================过滤器开始工作===========================
2020-01-05 16:11:04.030  INFO 7796 --- [nio-8080-exec-1] edu.duan.springboot.util.ApiLogUtil      : 请求:127.0.0.1,HTTP/1.1, POST, http://localhost:8080/api/user, headers={"content-length":"9","host":"localhost:8080","content-type":"application/json;charset=UTF-8","connection":"Keep-Alive","cache-control":"no-cache","accept-encoding":"gzip,deflate","accept":"*/*","user-agent":"Apache-HttpClient/4.5.2 (Java/1.8.0_152-release)"}, params={}, body={"uid":1}
2020-01-05 16:11:04.039  INFO 7796 --- [nio-8080-exec-1] edu.duan.springboot.http.LogInterceptor  : ===========================拦截器开始任务===========================
2020-01-05 16:11:04.039  INFO 7796 --- [nio-8080-exec-1] edu.duan.springboot.util.ApiLogUtil      : 请求:127.0.0.1,HTTP/1.1, POST, http://localhost:8080/api/user, headers={"content-length":"9","host":"localhost:8080","content-type":"application/json;charset=UTF-8","connection":"Keep-Alive","cache-control":"no-cache","accept-encoding":"gzip,deflate","accept":"*/*","user-agent":"Apache-HttpClient/4.5.2 (Java/1.8.0_152-release)"}, params={}, body={"uid":1}
2020-01-05 16:11:04.750  INFO 7796 --- [nio-8080-exec-1] edu.duan.springboot.util.ApiLogUtil      : 响应:response={"code":200,"msg":null,"body":[{"uid":1,"uname":"张三","uage":15}]}
2020-01-05 16:11:04.751  INFO 7796 --- [nio-8080-exec-1] edu.duan.springboot.http.LogInterceptor  : ===========================拦截器本次任务结束===========================
2020-01-05 16:11:04.756  INFO 7796 --- [nio-8080-exec-1] edu.duan.springboot.util.ApiLogUtil      : 响应:response={"code":200,"msg":null,"body":[{"uid":1,"uname":"张三","uage":15}]}

 

附:

BodyCachingHttpServletRequestWrapper

package edu.duan.springboot.http;


import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;

import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.function.BiConsumer;

@Slf4j
public class BodyCachingHttpServletRequestWrapper extends HttpServletRequestWrapper {


    private final Map<String, String[]> parameterMap = new HashMap<>();
    private byte[] bytes = new byte[]{};


    public BodyCachingHttpServletRequestWrapper(HttpServletRequest request) throws IOException {
        super(request);
        parameterMap.putAll(request.getParameterMap());
        try {
            bytes = IOUtils.toByteArray(request.getInputStream());
        } catch (IOException e) {
            log.error(e.getMessage(), e);
        }
    }


    public void customize(BiConsumer<HttpServletRequest, BodyCachingHttpServletRequestWrapper> handler) {
        handler.accept((HttpServletRequest) getRequest(), this);
    }

    public void addParameter(String name, String value) {
        if (name != null && value != null) {
            String[] parameterValues = getParameterValues(name);
            List<String> values = parameterValues != null ? Lists.newArrayList(parameterValues) : Lists.newArrayList();
            values.add(value);
            parameterMap.put(name, values.toArray(new String[0]));
        }
    }

    public void setParameter(String name, String value) {
        if (name != null) {
            if (value != null) {
                parameterMap.put(name, new String[]{value});
            } else {
                parameterMap.remove(name);
            }
        }
    }

    @Override
    public String getParameter(String name) {
        String[] values = parameterMap.get(name);
        return values != null ? values[0] : null;
    }

    @Override
    public Map<String, String[]> getParameterMap() {
        return parameterMap;
    }

    @Override
    public Enumeration<String> getParameterNames() {
        return Collections.enumeration(parameterMap.keySet());
    }

    @Override
    public String[] getParameterValues(String name) {
        return parameterMap.get(name);
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {
        final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
        return new ServletInputStream() {

            @Override
            public int read() throws IOException {
                return byteArrayInputStream.read();
            }

            @Override
            public boolean isFinished() {
                return false;
            }

            @Override
            public boolean isReady() {
                return false;
            }

            @Override
            public void setReadListener(ReadListener listener) {

            }

        };
    }

    @Override
    public BufferedReader getReader() throws IOException {
        return new BufferedReader(new InputStreamReader(getInputStream(), StandardCharsets.UTF_8));
    }

}
BodyCachingHttpServletResponseWrapper

package edu.duan.springboot.http;


import javax.servlet.ServletOutputStream;
import javax.servlet.WriteListener;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import java.io.ByteArrayOutputStream;
import java.io.IOException;

public class BodyCachingHttpServletResponseWrapper extends HttpServletResponseWrapper {


    private ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();

    /**
     * Constructs a response adaptor wrapping the given response.
     *
     * @param response The response to be wrapped
     * @throws IllegalArgumentException if the response is null
     */
    public BodyCachingHttpServletResponseWrapper(HttpServletResponse response) {
        super(response);
    }

    public byte[] getBytes() {
        return byteArrayOutputStream.toByteArray();
    }

    @Override
    public ServletOutputStream getOutputStream() throws IOException {
        return new ServletOutputStream() {
            @Override
            public void write(int b) throws IOException {
                byteArrayOutputStream.write(b);
                getResponse().getOutputStream().write(b);
            }

            @Override
            public boolean isReady() {
                return false;
            }

            @Override
            public void setWriteListener(WriteListener listener) {

            }
        };
    }

}
ApiLogUtil
package edu.duan.springboot.util;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import edu.duan.springboot.http.BodyCachingHttpServletRequestWrapper;
import edu.duan.springboot.http.BodyCachingHttpServletResponseWrapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Enumeration;
import java.util.Map;
import java.util.stream.Collectors;

@Slf4j
public class ApiLogUtil {


    public static void logRequest(BodyCachingHttpServletRequestWrapper requestWrapper) throws IOException {
        Enumeration<String> headersEnum = requestWrapper.getHeaderNames();
        JSONObject headers = new JSONObject();
        while (headersEnum.hasMoreElements()) {
            String name = headersEnum.nextElement();
            headers.put(name, requestWrapper.getHeader(name));
        }
        Map<String, String> params = requestWrapper.getParameterMap().entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, entry -> String.join(",", entry.getValue())));
        String body = IOUtils.toString(requestWrapper.getInputStream(), StandardCharsets.UTF_8);
        // String body = "";
        log.info("请求:{},{}, {}, {}, headers={}, params={}, body={}", NetworkUtil.getIpAddress(requestWrapper), requestWrapper.getProtocol(), requestWrapper.getMethod(), requestWrapper.getRequestURL(), JSON.toJSONString(headers), params, body);
    }

    public static void logResponse(BodyCachingHttpServletResponseWrapper responseWrapper) throws IOException {
        byte[] responseBody = responseWrapper.getBytes();
        String content = IOUtils.toString(responseBody, StandardCharsets.UTF_8.name());
        log.info("响应:response={}", content);
    }


}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值