gobo
gobo

Reputation: 320

Response for preflight has invalid HTTP status code 403

I am using as Server-side Spring-boot and providing a dummy service for test

where my ServiceCaller.java=

package com.user.server.mfw;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.handler.MappedInterceptor;


@RestController
public class ServiceCaller {
    @CrossOrigin(allowedHeaders="*",allowCredentials="true")
    @RequestMapping(value="/serviceCaller",method=RequestMethod.POST, headers="content-type=text/*")
    @ResponseBody
    String serviceListener(@RequestParam("serviceName") String serviceName,HttpSession session,HttpServletRequest theHttpServletReq ) throws IOException 
    {


        if(!serviceName.isEmpty())
        {
           byte[] encoded = Files.readAllBytes(Paths.get("C://Users//something//Desktop//asd.json"));
           return new String(encoded, "UTF-8");
        }
        return "gelemedi";
    }


    private void checkActiveSessionControl(HttpSession session)
    {   
        System.out.println("Session Id:" +  session.getId() +" // " + session.getCreationTime());
        if(session == null)
            System.out.println("Null");
        else if(session.isNew())
            System.out.println("New");
        else
            System.out.println("Old");
    }



}

where my client-side is a ionic framework and based on angular.js...

Controller.js

   $scope.getInfo = function() {
    $http({
        url: SERVER_ENDPOINT.url + '/serviceCaller',
        method: 'POST',
        params: {serviceName: 'deneme'},
        withCredentials: true
    }).then(function(result) {
      var alertPopup = $ionicPopup.alert({
        title: 'ALOHA!',
        template: 'dksjd ' + result
      });
      $scope.memberInfo = result.data.accountNumber;
    }, function() {
        var alertPopup = $ionicPopup.alert({
          title: ' failed!',
          template: 'da'
        });
      }

    );
  };

Basically I get a "invalid HTTP status code 403" when I use POST method instead of GET. However I would like to use POST for calling instead of GET. However I could not figure out where I am making a mistake....

any solution will be appreciated!

Upvotes: 1

Views: 6482

Answers (4)

Yogen Rai
Yogen Rai

Reputation: 3033

I found the explanation of preflight request very clear in answer to question Angularjs Post not sending headers to Spring JWT.

Since you are using Spring Security, you have to enable CORS at Spring Security level as well to allow it to leverage the configuration defined at Spring MVC level as:

@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.cors().and()...
    }
}

Here is very excellent tutorial explaining CORS support in Spring MVC framework.

This enables HttpMethod.Options request to include headers in preflight request.

Upvotes: 0

Vinny
Vinny

Reputation: 149

If your browser is sending a pre-flight OPTIONS request , all you have to do is to allow that in your WebSecurity configuration by allowing http OPTIONS.

.antMatchers(HttpMethod.OPTIONS, "/**").permitAll()

Upvotes: 7

Yannic Bürgmann
Yannic Bürgmann

Reputation: 6581

As shown in the AngularJS docs

XSRF is a technique by which an unauthorized site can gain your user's private data. Angular provides a mechanism to counter XSRF. When performing XHR requests, the $http service reads a token from a cookie (by default, XSRF-TOKEN) and sets it as an HTTP header (X-XSRF-TOKEN). Since only JavaScript that runs on your domain could read the cookie, your server can be assured that the XHR came from JavaScript running on your domain. The header will not be set for cross-domain requests.

so the default header is x-xsrf-token.

Add this filter in your websecurityconfiguration after CsrfFilter

public class CsrfHeaderFilter extends OncePerRequestFilter {
  @Override
  protected void doFilterInternal(HttpServletRequest request,
      HttpServletResponse response, FilterChain filterChain)
      throws ServletException, IOException {

    CsrfToken csrf = (CsrfToken) request.getAttribute(CsrfToken.class.getName());
    if (csrf != null) {
      Cookie cookie = WebUtils.getCookie(request, "XSRF-TOKEN");
      String token = csrf.getToken();
      if (cookie==null || token!=null && !token.equals(cookie.getValue())) {
        cookie = new Cookie("XSRF-TOKEN", token);
        cookie.setPath("/");
        response.addCookie(cookie);
      }
    }
    filterChain.doFilter(request, response);
  }
}

add the filter as shown here:

protected static class SecurityConfiguration extends WebSecurityConfigurerAdapter {
  @Override
  protected void configure(HttpSecurity http) throws Exception {
    http
      .httpBasic()...
      .and()
      .addFilterAfter(new CsrfHeaderFilter(), CsrfFilter.class);
  }
}

Upvotes: 1

Paulo Galdo Sandoval
Paulo Galdo Sandoval

Reputation: 2203

I think you're not passing any parameter using this anotation:

@CrossOrigin(allowedHeaders="*",allowCredentials="true")
@RequestMapping(value="/serviceCaller",method=RequestMethod.POST, headers="content-type=text/*")
@ResponseBody
String serviceListener(@RequestParam("serviceName") String serviceName,HttpSession session,HttpServletRequest theHttpServletReq ) throws IOException 
{

you should replace value="/serviceCaller" by value="/{serviceCaller}"

EDIT

please add this class to your project to solve the CORS problems

@Component
public class SimpleCORSFilter implements Filter {

@Override
public void init(FilterConfig fc) throws ServletException {}

@Override
public void doFilter(ServletRequest req, ServletResponse resp,
        FilterChain chain) throws IOException, ServletException {
    // TODO Auto-generated method stub
    HttpServletResponse response = (HttpServletResponse) resp;

    response.setHeader("Access-Control-Allow-Origin", "*");
    response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
    response.setHeader("Access-Control-Max-Age", "3600");
    response.setHeader("Access-Control-Allow-Headers", "x-requested-with");
    chain.doFilter(req, resp);
}

@Override
public void destroy() {}

}

Upvotes: 1

Related Questions