Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
245 views
in Technique[技术] by (71.8m points)

javascript - CORS POST request fails on Chrome, Safari and Firefox

I have a RESTfull Backend implemented with Spring security running on localhost:8080, a Login filter responds to login requests with a token placed in the headers, I didn’t even implemented an Endpoint method for that, It is all done by Spring Security magic by the following line of code:

  protected void configure(HttpSecurity http) throws Exception {
        // disable caching
        http.headers().cacheControl();

        http.csrf().disable() // disable csrf for our requests.
            .authorizeRequests()
            .antMatchers("/").permitAll()
            .antMatchers("/login").permitAll()
            .anyRequest().authenticated()
            .and()
            // We filter the api/login requests
            .addFilterBefore(new JWTLoginFilter("/login", authenticationManager()), UsernamePasswordAuthenticationFilter.class)
    …

The frontend is a Angularjs Nodejs NPM Bower project running a static http server on localhost:8000. On frontend I make a simple POST request as follows:

   $scope.login = function () {

    var data = {
        "username":$scope.username,
        "password":$scope.password
    }

    $http({
        url: baseUrl,
        method: "POST",
        data: data
    }).then(function (response) {
        console.log("###### SUCCESS #####");
        var headers = response.headers();
        console.log("headers: "+JSON.stringify(headers));
        var token = headers.authorization.split(" ")[1];
        console.log("token: "+token);
        $http.defaults.headers.common['Authorization'] = token;
        $location.path("/view1");

    }, function (responseData) {
        // called asynchronously if an error occurs
        // or server returns responseData with an error status.
        console.log("###### FAIL #####");
        console.log("Response: "+JSON.stringify(responseData));
        $window.alert("Failed to Login");
    });

This works like a charm in IE (also with curl, wget and python requests) but it miserably failing on Chrome and Safary. I know that those Browsers are blocking CORS POSTs, making the request empty as soon as the reach the backend, in fact I don’t see any data when I log out the request from backend. I tried every possible combination of:

Frontend side:

1) $http(method: POST)

2) $http.post(

3) Adding flags: Access-Control-Allow-Origin, Access-Control-Expose, etc.

4) Adding all possible header combination: ‘Content–Type’:’application/

Browser side:

1) Start chrome with flag: --disable-web-security

2) Installing Chrome extension CORS

Backend side:

1) Spring Security Disable csfr

2) Spring Security Permit all

3) Spring Security HttpMethod.OPTION

Nothing, NHOTING worked for me!

is there something I’m missing?

Is there another way to send POST requests?

EDIT

As discussed, I modified the classes as follow:

WebSecurityConfig:

        .antMatchers("/login").permitAll()
        .anyRequest().authenticated()
        .and()
        // We filter the api/login requests
        .addFilterBefore(new JWTLoginFilter("/login", authenticationManager()), UsernamePasswordAuthenticationFilter.class)
        .addFilterBefore(new CORSFilter(), BasicAuthenticationFilter.class)

and implemented the CORSFilter as suggestet.

I also add the WebConfig class as suggested:

@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**").allowedOrigins("http://localhost:8000")
        .allowedMethods("PUT", "POST");
    }
}

Because of the empty string the login filter throws:

com.fasterxml.jackson.databind.JsonMappingException: No content to map due to end-of-input

this will be chatched by spring security which denied the access.

I also triyed to move the frontend server on other ports then 8000 (4200, 7000, etc.) with no success.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

You need to enable Cors support in spring. In your WebConfig you can override addCorsMappings

@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**").allowedOrigins("http://localhost:4200");  //url of where angular is running.
    }
}

This enables cors for the whole application. You can also be more specific with your mappings allowing specific header, and methods.

@Override
public void addCorsMappings(CorsRegistry registry) {
    registry.addMapping("/api/**")
            .allowedOrigins("http://domain2.com")
            .allowedMethods("PUT", "DELETE")
            .allowedHeaders("header1", "header2", "header3");
}

You can also allow user @CrossOrgin at the class and method level

@CrossOrigin(origin = "http://domain2.com",
             maxAge = 3600)
public class ApiController {

}

http://docs.spring.io/spring/docs/current/spring-framework-reference/html/cors.html

https://spring.io/guides/gs/rest-service-cors/


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...