java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > Springboot过滤器修改response的header和body内容

Springboot应用中过滤器如何修改response的header和body内容

作者:彼岸花@开

这篇文章主要介绍了Springboot应用中过滤器如何修改response的header和body内容问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

Springboot过滤器修改response的header和body内容

springboot添加过滤器,继承Filter接口,实现doFilter方法

方法一,实现类增加注解@WebFilter,注解参数filterName表示过滤器名称,urlPatterns表示要过滤的url路径,在启动类增加注解@ServletComponentScan,表示能扫描到该类。

当有多个过滤器时,通过注解@Order,注解参数大小表示过滤器执行的县厚顺序,越小越先执行

@WebFilter(filterName = "responseFilter",urlPatterns = "/*")
public class ResponseFilter implements Filter

方法二,创建新配置类,添加 @Configuration 注解,将自定义Filter加入过滤链。

@Configuration
public class DefinedFilterConfig {
    @Bean
    public FilterRegistrationBean responseFilter() {
        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
        ResponseFilter responseFilter = new ResponseFilter();
        filterRegistrationBean.setFilter(responseFilter);
        filterRegistrationBean.addUrlPatterns("/*");//配置过滤规则
        filterRegistrationBean.setName("responseFilter");//设置过滤器名称
        filterRegistrationBean.setOrder(1);//执行次序
        return filterRegistrationBean;
    }
}

springboot在response中添加或者修改header

分两种情况

servletResponse.addHeader("XXX", "xxxx");

或者

servletResponse.setHeader("XXX", "xxxx");

两者的区别是addHeader不会覆盖,只会追加,会照成headerName重名

setHeader会覆盖重名的headerName

这样是可以添加成功

添加addHeader或者serHeader无效,是因为这和过滤器的处理流程以及对header的处理时机有关

首先过滤器链的处理流程是:进入到一个过滤器的doFitler方法中,处理一些逻辑,然后调用chain.doFilter(request, httpServletResponse);进入到过滤器链的下一个过滤器的doFilter方法中.当在过滤器链上最后一个过滤器的doFilter方法中调用chain.doFilter(request, httpServletResponse);时,将会把请求转发到Servlet中,再分配到对应的Controller的方法中。当从Controller的方法中退出,再回到最后一个过滤器的doFilter方法中之前,就将会把respone对象上的header写入到headerBuffer中。

所以,在chain.doFilter()方法之后,一方面是给response对象设置header不会成功,因为发现response对象的状态已经是commited状态,就不会再写入到headers里,另一方面,即便通过反射的方式写入了,也不会输出给客户端,因为headers已经处理过了。

springboot在response中修改body

spring提供了HttpServletResponse的包装类HttpServletResponseWrapper

首先需要继承该包装类,生成自定义包装类BodyCachingHttpServletResponseWrapper

public class BodyCachingHttpServletResponseWrapper extends HttpServletResponseWrapper {
    private ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
    private HttpServletResponse response;
    private PrintWriter pwrite;
    public BodyCachingHttpServletResponseWrapper(HttpServletResponse response) {
        super(response);
        this.response = response;
    }
    public byte[] getBytes() {
        if(pwrite != null) {
            pwrite.close();
            return byteArrayOutputStream.toByteArray();
        }
        if(byteArrayOutputStream != null) {
            try {
                byteArrayOutputStream.flush();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return byteArrayOutputStream.toByteArray();
    }
    @Override
    public ServletOutputStream getOutputStream() {
        return new ServletOutputStreamWrapper(this.byteArrayOutputStream , this.response);
    }
    @Override
    public PrintWriter getWriter() throws IOException {
        pwrite = new PrintWriter(new OutputStreamWriter(this.byteArrayOutputStream , this.response.getCharacterEncoding()));
        return pwrite;
    }
    private static class ServletOutputStreamWrapper extends ServletOutputStream {
        private ByteArrayOutputStream outputStream;
        private HttpServletResponse response;
        public ServletOutputStreamWrapper(ByteArrayOutputStream outputStream, HttpServletResponse response) {
            super();
            this.outputStream = outputStream;
            this.response = response;
        }
        @Override
        public boolean isReady() {
            return true;
        }
        @Override
        public void setWriteListener(WriteListener listener) {
        }
        @Override
        public void write(int b) throws IOException {
            this.outputStream.write(b);
        }
//        @Override
//        public void flush() throws IOException {
//            if (! this.response.isCommitted()) {
//                byte[] body = this.outputStream.toByteArray();
//                ServletOutputStream outputStream = this.response.getOutputStream();
//                outputStream.write(body);
//                outputStream.flush();
//            }
//        }
    }

然后通过过滤器,将自定义包装类BodyCachingHttpServletResponseWrappe传递到chain.doFilter(servletRequest, bodyCachingHttpServletResponseWrapper ),通过bodyCachingHttpServletResponseWrapper .getBytes()方式获得原有body的内容。

最后自己根据自己的逻辑代码,修改原有body的内容,重新写入输出流即可

public class ResponseFilter implements Filter {
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain)
            throws IOException, ServletException {
        BodyCachingHttpServletResponseWrapper responseWrapper = new BodyCachingHttpServletResponseWrapper((HttpServletResponse) servletResponse);
        //获取当前accessToken
        Optional<String> currentUserAccessTokenOptional = SecurityUtils.getCurrentUserAccessToken();
        String currentUserAccessToken = null;
        if(currentUserAccessTokenOptional.isPresent()) {
            currentUserAccessToken = currentUserAccessTokenOptional.get();
        }
        responseWrapper.addHeader("xuncai_access_token", currentUserAccessToken);
        chain.doFilter(servletRequest, responseWrapper);
        Optional<String> xTotalCount = Optional.ofNullable(responseWrapper.getHeader("X-Total-Count"));
        byte[] writeValueByte = null;
        if(xTotalCount.isPresent() && responseWrapper.getBytes() != null) {
            String currentTotal = xTotalCount.get();
            String currentData = new String(responseWrapper.getBytes());
            String writeValue = "{\"total\":"+currentTotal+",\"data\":"+currentData+"}";
            writeValueByte = writeValue.getBytes();
        }else {
            writeValueByte = responseWrapper.getBytes();
        }
        //重新写入输出流
        ServletOutputStream outputStream = servletResponse.getOutputStream();
        outputStream.write(writeValueByte);
        outputStream.flush();
        outputStream.close();
    }
}

经过测试,一切OK。

Springboot在绝大部分过滤器中修改请求头信息

实现方式

反射。

代码

package one.util;
import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.RequestFacade;
import org.apache.tomcat.util.http.MimeHeaders;
import org.springframework.util.ObjectUtils;
import javax.servlet.ServletRequestWrapper;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
public class HeadersEdit {
    private final HttpServletRequest request;
    private Map<String, String> modifyHeaders;
    private MimeHeaders itsHeaders;
    private boolean isDeleteKnown=true;
    public HeadersEdit(HttpServletRequest request){
        this.request=request;
        modifyHeaders= new HashMap<>();
        itsHeaders=getMimeHeaders(getRequestFacade());
    }
    private HttpServletRequest getRequestFacade(){
        try{
            Field field= ServletRequestWrapper.class.getDeclaredField("request");
            HttpServletRequest result;
            field.setAccessible(true);
            result=(HttpServletRequest)field.get((request));
            while(!(result instanceof RequestFacade)){
                result=(HttpServletRequest)field.get((result));
            }
            return result;
        }catch (Exception e){
            e.printStackTrace();
            throw new RuntimeException("HeadersEdit Error!");
        }
    }
    private MimeHeaders getMimeHeaders(HttpServletRequest requestFacade){
        try{
            Field itsRequest= RequestFacade.class.getDeclaredField("request");
            itsRequest.setAccessible(true);
            HttpServletRequest o5=(HttpServletRequest) itsRequest.get(requestFacade);
            Field coyoteField= Request.class.getDeclaredField("coyoteRequest");
            coyoteField.setAccessible(true);
            org.apache.coyote.Request coyoteRequest=(org.apache.coyote.Request) coyoteField.get(o5);
            Field headers=org.apache.coyote.Request.class.getDeclaredField("headers");
            headers.setAccessible(true);
            return (MimeHeaders)headers.get(coyoteRequest);
        }catch (Exception e){
            e.printStackTrace();
            throw new RuntimeException("HeadersEdit Error!");
        }
    }
    private boolean setValueExecutor(Map<String,String>newHeaders){
        String key;
        for(Map.Entry<String, String> entry : newHeaders.entrySet()){
            key=entry.getKey();
            if(itsHeaders.getHeader(key)!=null){
                if(isDeleteKnown){
                    itsHeaders.removeHeader(key);
                }else{
                    return false;
                }
            }
            itsHeaders.addValue(key).setString(entry.getValue());
        }
        return true;
    }
    public boolean setValue(String key,String value){
        if(ObjectUtils.isEmpty(key)||ObjectUtils.isEmpty(value)){
            return false;
        }
        Map<String,String>map=new HashMap<>();
        map.put(key,value);
        return setValueExecutor(map);
    }
    public boolean setValue(Map<String,String>newHeaders){
        if(ObjectUtils.isEmpty(newHeaders)){
            return false;
        }
        return setValueExecutor(newHeaders);
    }
    public void changeCheck(){
        isDeleteKnown=!isDeleteKnown;
    }
}

测试环境

springsecurity中的过滤器、以及普通的过滤器。

原理简述

许多过滤器都间接继承了ServletRequestWrapper类,它有个属性叫request。

springsecurity或者普通的过滤器类只是将其包装了多层,通过反射一层一层剥开即可。

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

您可能感兴趣的文章:
阅读全文