-spring-如何利用spring对filter中新增的属性进行封装

1 篇文章 0 订阅

问题背景:

插入一个:spring boot 如何利用log4j2 打印日志
工程源码 : spring-boot-package

公司在接收其他平台数据的时候,走的是restful api接口,但是有两个特殊要求。

  1. 需要对整个业务参数块进行加密
  2. 开发的接口需要 提供swagger ui
  • post请求 入参示例如下
  • 业务入参如下
{
	"token":"accessToken",
	"securityKey":"ss212313",
	"data":
		{
			"IMEI":"123",
			"state":"off"
		}
}
  • 实际传参则是直接将data 中的业务数据整块加密
{
	"token":"accessToken",
	"securityKey":"ss212313",
	"data": "加密后的数据"
}

swagger要能展示入参数据结构,接收参数的对象需要与 业务入参的数据结构一致。但实际http
请求传入的参数却是加密后的字符串
问题点:如何让spring 帮我们封装解密后的数据?

如何让spring 对我们新增的属性,自动完成对象封装

先处理一个简单的,比如get请求,如何在请求处理过程中添加属性,并让spring自动将封装到入参对象中。

  1. 如下设计2个入参对象
@Data
public class CommonVo {
  private String name;
  private Integer age;
}
@Data
public class CustomerVo {
  private String name;
  private Integer age;
  private String key;
  private Son son;
  @Data
  public class Son{
    private String fieldOne;
    private String fieldTwo;
  }
}
  1. 如下图所示,在代码中为其新增属性,并在转发后的请求中,能够将新增的属性自动封装到CustomerVo对象中
    在这里插入图片描述
  2. 比较常用的操作,通常是 通过Wrapper 对ServletRequest进行封装,这种封装,要获取新增的属性,需要通过 req.getParameter(key) 来获取,spring并不会对添加到Wrapper的属性,进行自动封装
public class RequestParameterWrapper extends HttpServletRequestWrapper {

    private Map<String, String[]> params = new HashMap();

    public RequestParameterWrapper(HttpServletRequest request) {
        super(request);
        //将现有parameter传递给params
        this.params.putAll(request.getParameterMap());
    }

    public void addParameter(String name, Object value) {
        if (value != null) {
            if (value instanceof String[]) {
                params.put(name, (String[]) value);
            } else if (value instanceof String) {
                params.put(name, new String[]{(String) value});
            } else {
                params.put(name, new String[]{String.valueOf(value)});
            }
        }
    }
    @Override
    public Map<String, String[]> getParameterMap() {
        return params;
    }
    @Override
    public String getParameter(String name) {
        String[] values = params.get(name);
        if (values == null || values.length == 0) {
            return null;
        }
        return values[0];
    }
    @Override
    public String[] getParameterValues(String name) {
        return params.get(name);
    }
}
  1. 只有知道spring 封装数据的原理,才能知道如何让spring将新增的属性封装到对象中
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

从这里开始需要注意跟踪了

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

如果你在上一步直接重写 getParameterNames() 方法也可以

我继续debug下去了,然后找的wrapper中的封装的HttpServletRequest中的 parameterNames了。因此走了弯路。

整理出来,对于parameter 的修改需要重写如下4个方法
	private Map<String, String[]> params = new HashMap();

    public RequestParameterWrapper(HttpServletRequest request) {
        super(request);
        //将现有parameter传递给params
        this.params.putAll(request.getParameterMap());
    }

    public void addParameter(String name, Object value) {
        if (value != null) {
            if (value instanceof String[]) {
                params.put(name, (String[]) value);
            } else if (value instanceof String) {
                params.put(name, new String[]{(String) value});
            } else {
                params.put(name, new String[]{String.valueOf(value)});
            }
        }
    }
    @Override
    public Map<String, String[]> getParameterMap() {
        return params;
    }
    @Override
    public String getParameter(String name) {
        String[] values = params.get(name);
        if (values == null || values.length == 0) {
            return null;
        }
        return values[0];
    }
    @Override
    public String[] getParameterValues(String name) {
        return params.get(name);
    }
    @Override
    public Enumeration<String> getParameterNames() {
        return Collections.enumeration(params.keySet());
    }
效果

在这里插入图片描述

接下来看一下post请求中,json参数的封装。

post请求,@RequestBody 的数据封装,会报如下的错误

I/O error while reading input message; nested exception is java.io.IOException: Stream closed
在这里出现的原因是,@RequestBody?

先到controller中转发之前,进行调试

在这里插入图片描述

直接到 DispatcherServlet

在这里插入图片描述

插入一点

GET请求 :

HttpServlet FrameworkServlet DispatcherServlet doGet service(HttpSer vletRequest,Htt pServletResponse) processRequest doService HttpServlet FrameworkServlet DispatcherServlet

POST请求 :

HttpServlet FrameworkServlet DispatcherServlet doPOST service(HttpSer vletRequest,Htt pServletResponse) processRequest doService HttpServlet FrameworkServlet DispatcherServlet

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

inputBuffer.streamClosed在这里插入图片描述
回到上一步,也就是说这个InputStream是不能再次读取的。
因为buffer 不执行 flip() 方法时,只能读取一次。转发前将HttpServletRequest封装到Controller里面的时候,已经进行过一次读取。现在封装到wrapper里面又到Request里面去读取一次,肯定要报错。
也是说,对于json数据的封装,wrapper要有自己的inputStream。回到上一步继续分析
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
如何重写 getInputStream(),我的想法是看一下代码里面有没有已重写过的例子可以参考。
在这里插入图片描述

最后的wrapper如下


public class RequestParameterWrapper extends HttpServletRequestWrapper {

    private Map<String, String[]> params = new HashMap();

    public RequestParameterWrapper(HttpServletRequest request) {
        super(request);
        //将现有parameter传递给params
        this.params.putAll(request.getParameterMap());
    }

    public void addParameter(String name, Object value) {
        if (value != null) {
            if (value instanceof String[]) {
                params.put(name, (String[]) value);
            } else if (value instanceof String) {
                params.put(name, new String[]{(String) value});
            } else {
                params.put(name, new String[]{String.valueOf(value)});
            }
        }
    }
    @Override
    public Map<String, String[]> getParameterMap() {
        return params;
    }
    @Override
    public String getParameter(String name) {
        String[] values = params.get(name);
        if (values == null || values.length == 0) {
            return null;
        }
        return values[0];
    }
    @Override
    public String[] getParameterValues(String name) {
        return params.get(name);
    }
    @Override
    public Enumeration<String> getParameterNames() {
        return Collections.enumeration(params.keySet());
    }


    private JSONObject jsons = new JSONObject();

    public void addJson(String key, Object obj) {
        jsons.put(key, obj);
    }

    @Override
    public ServletInputStream getInputStream() {
        MyServletInputStream coyoteInputStream = new MyServletInputStream(new ByteArrayInputStream(jsons.toJSONString().getBytes()));
        return coyoteInputStream;
    }

    public class MyServletInputStream extends ServletInputStream {
        private final InputStream sourceStream;
        private boolean finished = false;

        public MyServletInputStream(InputStream sourceStream) {
            Assert.notNull(sourceStream, "Source InputStream must not be null");
            this.sourceStream = sourceStream;
        }
        @Override
        public int read() throws IOException {
            int data = this.sourceStream.read();
            if (data == -1) {
                this.finished = true;
            }
            return data;
        }
        @Override
        public int available() throws IOException {
            return this.sourceStream.available();
        }
        @Override
        public void close() throws IOException {
            super.close();
            this.sourceStream.close();
        }
        @Override
        public boolean isFinished() {
            return this.finished;
        }
        @Override
        public boolean isReady() {
            return true;
        }
        @Override
        public void setReadListener(ReadListener readListener) {
            throw new UnsupportedOperationException();
        }
    }
}

在这里插入图片描述

汇总

其实将controller中转发前的数据封装放到filer中,也是完全可以的,只需要判断一下,对于json参数,则按ServletInputStream中封装,其他的按parameters中封装就可以了。
我之所以写着controller层封装,是因为我的接口格式,对外只提供一个请求,然后通过字段 action进行业务转发。
其实总的来说:还是对于 HttpServletRequestWrapper 不够熟悉,所以才碰到这么多问题。

下面是对 封装请求能够重写的方法进行了一个汇总

public class RequestParameterWrapperTest extends HttpServletRequestWrapper {

    public RequestParameterWrapperTest(HttpServletRequest request) {
        super(request);
    }

    /**
     * 重写的方法,主要来自两大类
     * HttpServletRequestWrapper
     * ServletRequestWrapper
     *
     * @return
     */

    /**
     * parameter 相关,原本是 封装在RequestFacade 中的Request中的coyoteRequest[Request]中的 ParameterMap<String, String[]> parameterMap中
     * @param name
     * @return
     */
    @Override
    public String getParameter(String name) {
        return super.getParameter(name);
    }
    @Override
    public Map<String, String[]> getParameterMap() {
        return super.getParameterMap();
    }
    @Override
    public Enumeration<String> getParameterNames() {
        return super.getParameterNames();
    }
    @Override
    public String[] getParameterValues(String name) {
        return super.getParameterValues(name);
    }


    /**
     * 默认是保存在  Request 中的 Map<String, Object> attributes中
     * @param name
     * @param o
     */
    @Override
    public void setAttribute(String name, Object o) {
        super.setAttribute(name, o);
    }
    @Override
    public void removeAttribute(String name) {
        super.removeAttribute(name);
    }
    @Override
    public Object getAttribute(String name) {
        return super.getAttribute(name);
    }
    @Override
    public Enumeration<String> getAttributeNames() {
        return super.getAttributeNames();
    }


//    @Override
//    public ServletInputStream getInputStream() throws IOException {
//        return super.getInputStream();
//    }

    /**
     * header 相关
     {
     "cookie": "JSESSIONID=BEC4AAAD417148E4AA60A98AA425439C",
     "postman-token": "36f065f0-7ec0-4908-8189-4943c690b52b",
     "host": "localhost:8080",
     "connection": "keep-alive",
     "cache-control": "no-cache",
     "accept-encoding": "gzip, deflate, br",
     "user-agent": "PostmanRuntime/7.25.0",
     "accept": "*//*"
    }
     * @param name
     * @return
     */
    @Override
    public String getHeader(String name) {
        return super.getHeader(name);
    }
    @Override
    public Enumeration<String> getHeaders(String name) {
        return super.getHeaders(name);
    }
    @Override
    public Enumeration<String> getHeaderNames() {
        return super.getHeaderNames();
    }
    @Override
    public int getIntHeader(String name) {
        return super.getIntHeader(name);
    }
    @Override
    public long getDateHeader(String name) {
        return super.getDateHeader(name);
    }


    /**
     * session相关
     * @return
     */
    @Override
    public String getRequestedSessionId() {
        return super.getRequestedSessionId();
    }
    @Override
    public HttpSession getSession(boolean create) {
        return super.getSession(create);
    }
    @Override
    public HttpSession getSession() {
        return super.getSession();
    }
    @Override
    public String changeSessionId() {
        return super.changeSessionId();
    }
    @Override
    public boolean isRequestedSessionIdValid() {
        return super.isRequestedSessionIdValid();
    }
    @Override
    public boolean isRequestedSessionIdFromCookie() {
        return super.isRequestedSessionIdFromCookie();
    }
    @Override
    public boolean isRequestedSessionIdFromURL() {
        return super.isRequestedSessionIdFromURL();
    }
    @Override
    public boolean isRequestedSessionIdFromUrl() {
        return super.isRequestedSessionIdFromUrl();
    }


    // UTF-8
    @Override
    public String getCharacterEncoding() {
        return super.getCharacterEncoding();
    }
    @Override
    public void setCharacterEncoding(String enc) throws UnsupportedEncodingException {
        super.setCharacterEncoding(enc);
    }
    // [{"httpOnly":false,"maxAge":-1,"name":"JSESSIONID","secure":false,"value":"BEC4AAAD417148E4AA60A98AA425439C","version":0}]
    @Override
    public Cookie[] getCookies() {
        return super.getCookies();
    }

    // name=lisi&age=18
    @Override
    public String getQueryString() {
        return super.getQueryString();
    }
    // /demo/getAppendFieldForwoard
    @Override
    public String getRequestURI() {
        return super.getRequestURI();
    }
    // http://localhost:8080/demo/getAppendFieldForwoard
    @Override
    public StringBuffer getRequestURL() {
        return super.getRequestURL();
    }
    // /demo
    @Override
    public String getContextPath() {
        return super.getContextPath();
    }
    // /getAppendFieldForwoard
    @Override
    public String getServletPath() {
        return super.getServletPath();
    }
    // localhost
    @Override
    public String getServerName() {
        return super.getServerName();
    }
    // 8080
    @Override
    public int getServerPort() {
        return super.getServerPort();
    }

    @Override
    public String getRemoteAddr() {
        return super.getRemoteAddr();
    }
    @Override
    public String getRemoteHost() {
        return super.getRemoteHost();
    }
    @Override
    public int getRemotePort() {
        return super.getRemotePort();
    }
    @Override
    public String getRemoteUser() {
        return super.getRemoteUser();
    }

    @Override
    public Locale getLocale() {
        return super.getLocale();
    }
    @Override
    public int getLocalPort() {
        return super.getLocalPort();
    }
    @Override
    public String getLocalName() {
        return super.getLocalName();
    }
    @Override
    public String getLocalAddr() {
        return super.getLocalAddr();
    }
    @Override
    public Enumeration<Locale> getLocales() {
        return super.getLocales();
    }


    @Override
    public String getPathTranslated() {
        return super.getPathTranslated();
    }
    @Override
    public Principal getUserPrincipal() {
        return super.getUserPrincipal();
    }

    @Override
    public String getProtocol() {
        return super.getProtocol();
    }
    @Override
    public String getScheme() {
        return super.getScheme();
    }


    @Override
    public String getRealPath(String path) {
        return super.getRealPath(path);
    }


    @Override
    public String getPathInfo() {
        return super.getPathInfo();
    }
    @Override
    public String getAuthType() {
        return super.getAuthType();
    }
    @Override
    public HttpServletMapping getHttpServletMapping() {
        return super.getHttpServletMapping();
    }
    @Override
    public String getMethod() {
        return super.getMethod();
    }
    @Override
    public boolean isUserInRole(String role) {
        return super.isUserInRole(role);
    }
    @Override
    public boolean authenticate(HttpServletResponse response) throws IOException, ServletException {
        return super.authenticate(response);
    }
    @Override
    public void login(String username, String password) throws ServletException {
        super.login(username, password);
    }
    @Override
    public void logout() throws ServletException {
        super.logout();
    }
    @Override
    public Collection<Part> getParts() throws IOException, ServletException {
        return super.getParts();
    }
    @Override
    public Part getPart(String name) throws IOException, ServletException {
        return super.getPart(name);
    }
    @Override
    public <T extends HttpUpgradeHandler> T upgrade(Class<T> httpUpgradeHandlerClass) throws IOException, ServletException {
        return super.upgrade(httpUpgradeHandlerClass);
    }
    @Override
    public PushBuilder newPushBuilder() {
        return super.newPushBuilder();
    }
    @Override
    public Map<String, String> getTrailerFields() {
        return super.getTrailerFields();
    }
    @Override
    public boolean isTrailerFieldsReady() {
        return super.isTrailerFieldsReady();
    }
    @Override
    public ServletRequest getRequest() {
        return super.getRequest();
    }
    @Override
    public void setRequest(ServletRequest request) {
        super.setRequest(request);
    }
    @Override
    public int getContentLength() {
        return super.getContentLength();
    }
    @Override
    public long getContentLengthLong() {
        return super.getContentLengthLong();
    }
    @Override
    public String getContentType() {
        return super.getContentType();
    }
    @Override
    public BufferedReader getReader() throws IOException {
        return super.getReader();
    }
    @Override
    public boolean isSecure() {
        return super.isSecure();
    }
    @Override
    public RequestDispatcher getRequestDispatcher(String path) {
        return super.getRequestDispatcher(path);
    }
    @Override
    public ServletContext getServletContext() {
        return super.getServletContext();
    }
    @Override
    public AsyncContext startAsync() throws IllegalStateException {
        return super.startAsync();
    }
    @Override
    public AsyncContext startAsync(ServletRequest servletRequest, ServletResponse servletResponse) throws IllegalStateException {
        return super.startAsync(servletRequest, servletResponse);
    }
    @Override
    public boolean isAsyncStarted() {
        return super.isAsyncStarted();
    }
    @Override
    public boolean isAsyncSupported() {
        return super.isAsyncSupported();
    }
    @Override
    public AsyncContext getAsyncContext() {
        return super.getAsyncContext();
    }
    @Override
    public boolean isWrapperFor(ServletRequest wrapped) {
        return super.isWrapperFor(wrapped);
    }
    @Override
    public boolean isWrapperFor(Class<?> wrappedType) {
        return super.isWrapperFor(wrappedType);
    }
    @Override
    public DispatcherType getDispatcherType() {
        return super.getDispatcherType();
    }
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值