Summary

In my app,I want do more granular authentication In AccessDecisionManager By HttpServletRequest Body data.But HttpServletRequest Body data only get once。in other words, I implemented AOP to encapsulate HttpServletRequest, which can read the body data multiple times in the Aspect

Actual Behavior

If I read HttpServletRequest Body data once, I won't be able to read it later(Controller can‘t get Param!)

Expected Behavior

I do something but is very stupid。

@Component
public class SecAccessDecisionManager implements AccessDecisionManager {
    private static final Logger log = LoggerFactory.getLogger(SecAccessDecisionManager.class);
    @Override
    public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException {
        if (null == configAttributes) {
            return;
        }
        FilterInvocation fi = (FilterInvocation) object;
        HttpServletRequest request = fi.getHttpRequest();
//        RequestAttributes ra = RequestContextHolder.getRequestAttributes();
//        ServletRequestAttributes sra = (ServletRequestAttributes) ra;
//        HttpServletRequest request = sra.getRequest();
//        try {
//            System.out.println(request.getReader());
//        } catch (IOException e) {
//            e.printStackTrace();
//        }
        Iterator<ConfigAttribute> iterator = configAttributes.iterator();
        while (iterator.hasNext()) {
        }
        throw new AccessDeniedException("...");
    }
    @Override
    public boolean supports(ConfigAttribute attribute) {
        return true;
    }
    @Override
    public boolean supports(Class<?> clazz) {
        return true;
    }
}

Description: Package request And Support multiple reads

public class BodyReaderHttpServletRequestWrapper extends HttpServletRequestWrapper {

    private final byte[] body;
    private String bodyStr;

    public BodyReaderHttpServletRequestWrapper(HttpServletRequest request) throws IOException {
        super(request);
        String bodyString = getBodyString(request);
        body = bodyString.getBytes(Charset.forName("UTF-8"));
        bodyStr=bodyString;
    }

    public String getBodyStr() {
        return bodyStr;
    }

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

        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 readListener) {
            }
        };
    }


    public  String getBodyString(HttpServletRequest request) throws IOException {
        StringBuilder sb = new StringBuilder();
        InputStream inputStream = null;
        BufferedReader reader = null;
        try {
            inputStream = request.getInputStream();
            reader = new BufferedReader(
                    new InputStreamReader(inputStream, Charset.forName("UTF-8")));

            char[] bodyCharBuffer = new char[1024];
            int len = 0;
            while ((len = reader.read(bodyCharBuffer)) != -1) {
                sb.append(new String(bodyCharBuffer, 0, len));
            }
        } 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();
    }
}

Description: my Filter

@Component
@WebFilter(filterName = "MessageFilter", urlPatterns = {"/*"})

public class MessageFilter extends OncePerRequestFilter {
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        ServletRequest requestWrapper = null;
        requestWrapper = new BodyReaderHttpServletRequestWrapper(request);
        if (null == requestWrapper) {
            filterChain.doFilter(request, response);
        } else {
            filterChain.doFilter(requestWrapper, response);
        }
    }
}

Description: Aspect

@Aspect
@Component
public class NotifyAspect {
    private final Logger logger = LoggerFactory.getLogger(NotifyAspect.class);
    // Pointcut
    @Pointcut("execution(* com.zeusas.cloud.dcc.controller.*.*(..)))")
    public void excude() {
    }
    @Around("excude()")
    public Object loggingAround(ProceedingJoinPoint pjp){
        RequestAttributes ra = RequestContextHolder.getRequestAttributes();
        ServletRequestAttributes sra = (ServletRequestAttributes) ra;
        HttpServletRequest request = sra.getRequest();
        Object object = null;
        try {
            logger.info(((BodyReaderHttpServletRequestWrapper) request).getBodyStr());
            object = pjp.proceed();
            logger.info(((BodyReaderHttpServletRequestWrapper) request).getBodyStr());
            //TODO 
            }
        } catch (Throwable e) {
            logger.info("throwing logging");
            e.printStackTrace();
        }
        logger.info("after logging");
        return object;
    }
}

In AOP,I can get HttpServletRequest body data many times,And my Filter add Security FilterChain.

Configuration

@Override
protected void configure(HttpSecurity http) throws Exception {
        MessageFilter messageFilter = new MessageFilter();
//        messageFilter.setFailureHandler(new SecAuthenticationFailureHandler());
        // add my Filter
        http.addFilterBefore(messageFilter,UsernamePasswordAuthenticationFilter.class);
        ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry = http.authorizeRequests();
        registry.antMatchers(HttpMethod.OPTIONS, "/**").denyAll();
        registry.and().rememberMe();
        registry.and().formLogin().loginPage("/login").defaultSuccessUrl("/", true)
                .usernameParameter("username").passwordParameter("password")
                .and().logout();
        registry.anyRequest().authenticated();
        registry.and().exceptionHandling().accessDeniedHandler(secAccessDeniedHandler);
        registry.withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
            @Override
            public <O extends FilterSecurityInterceptor> O postProcess(O o) {
                o.setSecurityMetadataSource(securityMetadataSource);
                o.setAccessDecisionManager(secAccessDecisionManager);
                return o;
            }
        });
        registry.and().headers().frameOptions().disable();
        http.csrf().disable();
}

Question

in the SecAccessDecisionManager,can not get body data multiple. how to get HttpServletRequest Body data multiple times?looking forward to your reply.Thanks

Comment From: rwinch

Thanks for getting in touch, but it feels like this is a question that would be better suited to Stack Overflow. As mentioned in the guidelines for contributing, we prefer to use GitHub issues only for bugs and enhancements. Feel free to update this issue with a link to the re-posted question (so that other people can find it) or add some more details if you feel this is a genuine bug.