若依项目中需要在JwtAuthenticationTokenFilter的doFilterInternal方法中获取request中body中的toklen做权限验证,发现了这个问题getRead() has already been called for this request/getInputStream() has already been called for this request

原因是request.getInputStream()或request.getReader()获取到请求内容后,无法再调request.getParameter()获取请求内容。即对该类型的请求,三个方法互斥,只能调其中一个。

解决方法就是增加MyRequestWrapper类进行处理

getReader()能获取到application/json里面的参数,还有一种情况就是multipart/form-data传进来的参数,这个可以直接用request.getParameter获取。

MyRequestWrapper

package com.ruoyi.framework.security.filter;

import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.*;

public class MyRequestWrapper extends HttpServletRequestWrapper {

    private String body = null;
    private String token = null;

    public MyRequestWrapper(HttpServletRequest request, int type) throws IOException {
        super(request);
        if (type == 1) {
            this.token = request.getParameter("token");
        } else if (type == 2) {
            this.body = RequestReadUtils.read(request);
        }

    }

    public String getBody() {
        return body;
    }

    public String getToken() {
        return token;
    }


    @Override
    public ServletInputStream getInputStream() {
        final ByteArrayInputStream bais = new ByteArrayInputStream(body.getBytes());
        return new ServletInputStream() {

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

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

            @Override
            public void setReadListener(ReadListener readListener) {

            }

            @Override
            public int read() {
                return bais.read();
            }
        };
    }

    @Override
    public BufferedReader getReader() {
        return new BufferedReader(new InputStreamReader(this.getInputStream()));
    }

    @Override
    public String getParameter(String name) {
        return this.getToken();
    }

}

RequestReadUtils

package com.ruoyi.framework.security.filter;

import javax.servlet.http.HttpServletRequest;
import java.io.*;

public class RequestReadUtils {

    private static final int BUFFER_SIZE = 1024 * 8;

    public static String read(HttpServletRequest request) throws IOException {
        BufferedReader bufferedReader = request.getReader();
        StringWriter writer = new StringWriter();
        write(bufferedReader, writer);
        return writer.getBuffer().toString();
    }

    public static long write(Reader reader, Writer writer) throws IOException {
        return write(reader, writer, BUFFER_SIZE);
    }

    public static long write(Reader reader, Writer writer, int bufferSize) throws IOException {
        int read;
        long total = 0;
        char[] buf = new char[bufferSize];
        while ((read = reader.read(buf)) != -1) {
            writer.write(buf, 0, read);
            total += read;
        }
        return total;
    }
}

JwtAuthenticationTokenFilter

package com.ruoyi.framework.security.filter;


/**
 * token过滤器 验证token有效性
 *
 * @author ruoyi
 */

@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
    @Autowired
    private TokenService tokenService;
    @Autowired
    private AppService appService;
    @Autowired
    private ISysUserService sysUserService;

    private byte[] body;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
            throws ServletException, IOException {

        if (request.getRequestURI().startsWith("/tabaoApi")) {
            String token = null;
            MyRequestWrapper requestWrapper = null;
            if (request.getContentType().startsWith("multipart/form-data")) {
                requestWrapper = new MyRequestWrapper(request, 1);
                token = requestWrapper.getToken();
            } else if (request.getContentType().startsWith("application/json")) {
                requestWrapper = new MyRequestWrapper(request, 2);
                token = JSONObject.parseObject(requestWrapper.getBody()).getString("token");
            }

            if (token != null) {
                SysUser sysUser = appService.verifyAppUserToken(token);
                if (sysUser != null) {
                    UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(sysUser, null, null);
                    authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                    SecurityContextHolder.getContext().setAuthentication(authenticationToken);
                    chain.doFilter(requestWrapper, response);
                }
            }
        } else {
            LoginUser loginUser = tokenService.getLoginUser(request);
            if (StringUtils.isNotNull(loginUser) && StringUtils.isNull(SecurityUtils.getAuthentication())) {
                tokenService.verifyToken(loginUser);
                UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities());
                authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                SecurityContextHolder.getContext().setAuthentication(authenticationToken);
            }
            chain.doFilter(request, response);
        }
    }
}

我这还遇到查询的列表需要分页的情况:
方法1.这个可以在doFilterInternal方法中使用request获取,然后放在requestWrapper.setAttribute(“pageNum”, request.getParameter(“pageNum”))中,在需要的地方ServletUtils.getRequestAttributes().getAttribute(“pageNum”, 0));获取。
方法2.将pageNum和pageSize放在实体类中,然后获取。

Logo

快速构建 Web 应用程序

更多推荐