解决在Filter中读取Request中的流后, 然后再Control中读取不到的做法

Posted 阿奇XS

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了解决在Filter中读取Request中的流后, 然后再Control中读取不到的做法相关的知识,希望对你有一定的参考价值。

摘要: 大家知道, StringMVC中@RequestBody是读取的流的方式, 如果在之前有读取过流后, 发现就没有了.

我们来看一下核心代码: filter中主要做的事情, 就是来校验请求是否合法, 是否有篡改过值.

@Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException 

        if (Boolean.valueOf(authentication)) 
            HttpServletRequest httpServletRequest = (HttpServletRequest) request;
            if (BasicdataCommonsConstant.POST.equalsIgnoreCase(httpServletRequest.getMethod())) 
                // 防止流读取一次后就没有了, 所以需要将流继续写出去
                ServletRequest requestWrapper = new BodyReaderHttpServletRequestWrapper(httpServletRequest);
                String body = HttpHelper.getBodyString(requestWrapper);
                if (StringUtils.isBlank(body)) 
                    LOGGER.error("非法请求, 没有APP_KEY, APP_SECRET");
                    OutWriterUtil.outJson(response, gson.toJson(new Response(ErrorCode.AUTH_ERROR_ILLEGAL_FROMTYPE)));
                    return;
                
                Map<String, String> parameters = gson.fromJson(body, new TypeToken<Map<String, String>>() 
                .getType());

                String APP_KEY = parameters.get("appKey");
                String APP_SECRET = parameters.get("appSecret");

                TAuthUser authUser = authUserMap.get(APP_KEY);
                if (authUser == null) 
                    LOGGER.error("非法请求, 没有APP_KEY, APP_SECRET");
                    OutWriterUtil.outJson(response, gson.toJson(new Response(ErrorCode.AUTH_ERROR_ILLEGAL_FROMTYPE)));
                    return;
                 else if (StringUtils.isBlank(APP_SECRET)) 
                    LOGGER.error("非法请求, APP_SECRET为空, user=", gson.toJson(authUser));
                    OutWriterUtil.outJson(response, gson.toJson(new Response(ErrorCode.AUTH_ERROR_FROMTYPE_AND_KEY_CANNT_BE_NULL)));
                    return;
                 else if (!APP_SECRET.equals(authUser.getAppSecret())) 
                    LOGGER.error("非法请求: 没有APP_KEY, APP_SECRET不匹配. user=, password=, name=", APP_KEY, APP_SECRET, authUser.getName());
                    OutWriterUtil.outJson(response, gson.toJson(new Response(ErrorCode.AUTH_ERROR_ILLEGAL_KEY)));
                    return;
                
                String SIGNATURE = parameters.get("signature");
                // 对参数进行签名
                String md5 = SignatureUtil.decryptSignature(parameters);
                if (!md5.equals(SIGNATURE)) 
                    LOGGER.error("非法请求, signature =", SIGNATURE);
                    OutWriterUtil.outJson(response, gson.toJson(new Response(ErrorCode.AUTH_ERROR_SIGNATURE_ERROR)));
                    return;
                
                threadLocalUser.setAuthUser(authUser);
                chain.doFilter(requestWrapper, response);
            
        
        chain.doFilter(request, response);
    


大家都知道, 流只能读一次, 读了就没有了, 为了后面的代码还能够取得流, 我们应该还需要将其写出去才行.

所以, 我新建立了一个类. 看代码:

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.Charset;

/**
 * Created with antnest-platform
 * User: chenyuan
 * Date: 12/31/14
 * Time: 8:49 PM
 */
public class BodyReaderHttpServletRequestWrapper extends HttpServletRequestWrapper 

    private final byte[] body;

    public BodyReaderHttpServletRequestWrapper(HttpServletRequest request) throws IOException 
        super(request);
        body = HttpHelper.getBodyString(request).getBytes(Charset.forName("UTF-8"));
    

    @Override
    public BufferedReader getReader() throws IOException 
        return new BufferedReader(new InputStreamReader(getInputStream()));
    

    @Override
    public ServletInputStream getInputStream() throws IOException 

        final ByteArrayInputStream bais = new ByteArrayInputStream(body);

        return new ServletInputStream() 

            @Override
            public int read() throws IOException 
                return bais.read();
            

            @Override
            public boolean isFinished() 
                return false;
            

            @Override
            public boolean isReady() 
                return false;
            

            @Override
            public void setReadListener(ReadListener readListener) 

            
        ;
    

import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;

/**
 * Created with antnest-platform
 * User: chenyuan
 * Date: 12/24/14
 * Time: 10:39 AM
 */
public class HttpHelper 

    /**
     * 获取请求Body
     *
     * @param request
     * @return
     */
    public static String getBodyString(ServletRequest request) 
        StringBuilder sb = new StringBuilder();
        InputStream inputStream = null;
        BufferedReader reader = null;
        try 
            inputStream = request.getInputStream();
            reader = new BufferedReader(new InputStreamReader(inputStream, Charset.forName("UTF-8")));
            String line = "";
            while ((line = reader.readLine()) != null) 
                sb.append(line);
            
         catch (IOException e) 
            e.printStackTrace();
         finally 
            if (inputStream != null) 
                try 
                    inputStream.close();
                 catch (IOException e) 
                    e.printStackTrace();
                
            
            if (reader != null) 
                try 
                    reader.close();
                 catch (IOException e) 
                    e.printStackTrace();
                
            
        
        return sb.toString();
    


请注意这里的编码, 最好将其转换成UTF-8的编码格式, 不然你获取到的中文则会使乱码的. 我自己也习惯于UTF-8的编码.

这样子就应该差不多了哦~

以上是关于解决在Filter中读取Request中的流后, 然后再Control中读取不到的做法的主要内容,如果未能解决你的问题,请参考以下文章

多次读取请求request里数据

Spring专题「Web请求读取系列」如何构建一个可重复读取的Request的流机制

#yyds干货盘点#☕并发技术系列「Web请求读取系列」如何构建一个可重复读取的Request的流机制

nodejs的流总结

如何在使用 CryptoPP 解密流后执行 unpadding

使用 EJABBERD-BUSINESS 版本在 SMACK 中恢复流后读取“重新绑定”数据包