3

I am using a HMAC Authentication filter. In the filter when I access my POST Request Body, I am able to get the XML in it. When I try to access the XML in the controller I am get a blank string. The xmlString in the filter is giving the proper XML but the xmlString in the controller is giving blank string. I am using Spring MVC.

My filter is:

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        HttpServletResponse httpResponse = (HttpServletResponse) response;

        String authorisation = httpRequest.getHeader("authorization");
        String accessKeyId = authorisation.split(":")[0].split(" ")[1];
        String signature = authorisation.split(":")[1];
        String secretAccessKey = getSecretAccessKey(accessKeyId);

        InputStream xmlStream = httpRequest.getInputStream();
        String xmlString = IOUtils.toString(xmlStream, "UTF-8");
        String encodedXml = new String();
        try {
            encodedXml = HMACEncoder.calculateHMAC(xmlString, secretAccessKey);
        } catch (SignatureException e) {
            e.printStackTrace();
        }
        if (!signature.equals(encodedXml))
            httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED);
        else
            chain.doFilter(request, response);
    }

and my controller is:

@RequestMapping(value = "/user", method = RequestMethod.POST)
public String fetchUserString(HttpServletRequest request) {
    InputStream xml = null;
    String xmlString = null;
    try {
        xml = request.getInputStream();
        xmlString = IOUtils.toString(xml, "UTF-8");
    } catch (IOException e) {
        e.printStackTrace();
    }
    return xmlString;
}
khateeb
  • 5,265
  • 15
  • 58
  • 114

2 Answers2

1

As praki suggested, I put the XML in a request attribute and in my controller I got it from the request attribute and then put the XML in a ModelAttribute. Here is my code:

My filter:

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
    HttpServletRequest httpRequest = (HttpServletRequest) request;
    HttpServletResponse httpResponse = (HttpServletResponse) response;
    if (httpRequest.getMethod().equalsIgnoreCase("POST")) {    
        String authorisation = httpRequest.getHeader("authorization");
        String accessKeyId = authorisation.split(":")[0].split(" ")[1];
        String signature = authorisation.split(":")[1];
        String secretAccessKey = getSecretAccessKey(accessKeyId);    
        ServletInputStream servletStream = httpRequest.getInputStream();
        String xmlString = IOUtils.toString(servletStream);
        String encodedXml = new String();
        try {
            encodedXml = HMACEncoder.calculateHMAC(xmlString, secretAccessKey);
        } catch (SignatureException e) {
            e.printStackTrace();
        }
        httpRequest.setAttribute("content", xmlString);
        if (!signature.equals(encodedXml))
            httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED);
        else
            chain.doFilter(request, response);
    } else
        chain.doFilter(request, response);
}

and my controller:

@RequestMapping(value = "/add", method = RequestMethod.POST, produces = "application/xml")
public @ResponseBody Catalog addCatalog(@ModelAttribute("catalog") Catalog catalog) {
    logger.info("In addCatalog with " + catalog);
    catalog.getHeader().setOrganizationId(IConstant.ORGANIZATION_ID_BSL);
    try {
        catalog = catalogService.addCatalogIntegration(catalog);
    } catch (Exception e) {
        e.printStackTrace();
    }
    logger.info("Returning from addCatalog with " + catalog);
    return catalog;
}

@ModelAttribute("catalog")
private Catalog getCatalog(HttpServletRequest request) throws Exception {
    Catalog catalog = new Catalog();
    try {
        String xml = (String) request.getAttribute("content");
        if (xml != null) {
            JAXBContext jaxbContext = JAXBContext.newInstance(Catalog.class);
            Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
            catalog = (Catalog) unmarshaller.unmarshal(new StringReader(xml));
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
    return catalog;
}
khateeb
  • 5,265
  • 15
  • 58
  • 114
0

You can't read the stream twice, comment the code in filter and read only in controller.

whoami
  • 1,517
  • 1
  • 21
  • 29
  • I need to be able to read the stream twice. In my filter, I have to check whether the encoding was done properly. Is there any way I can read it twice or put the original stream back to the request? – khateeb May 07 '15 at 05:38
  • Oh OK... No way to read the stream twice or put the contents back to stream as far as I know... So, read the stream in filter, and set the contents in request attribute. Now in your controller, read the contents from the request attribute rather than reading from stream. – whoami May 07 '15 at 05:42
  • why don't you use the header to hold your whole xml (som how encoded) needed for authorisation ? Here is nice example which could suit to your needs. https://github.com/philipsorst/angular-rest-springsecurity – Josef Prochazka May 07 '15 at 07:29
  • 1
    Another option is a cached version of the stream: [LINK TO THE EXAMPLE](https://stackoverflow.com/a/17129256/3044117) – Eduardo Jul 05 '17 at 17:56