1

I've been trying to build relatively simple app with angular and jersey REST on the back end. I've managed to make some communication between the two but when I try to implement security as by following this answer Best practice for REST token-based authentication with JAX-RS and Jersey I get some strange behaviour.

When I try to make a POST request from Angular app (localhost:4200) I get 403 Forbidden (Response to preflight request doesn't pass access control check) without even executing ContainerRequestFilter or ContainerResponseFilter.

When I send exactly the same request with POSTMAN everything works fine. Every filter gets called and authentication works fine.

Here are my classes:

@Provider
public class CorsFilter implements ContainerResponseFilter {

@Override
public void filter(ContainerRequestContext request,
        ContainerResponseContext response) throws IOException {

    System.out.println("cors");
    response.getHeaders().add("Access-Control-Allow-Origin", "*");
    response.getHeaders().add("Access-Control-Allow-Headers",
            "origin, content-type, accept, authorization");
    response.getHeaders().add("Access-Control-Allow-Credentials", "true");
    response.getHeaders().add("Access-Control-Allow-Methods",
            "GET, POST, PUT, DELETE, OPTIONS, HEAD");
    System.out.println(response.getHeaders());
}

REST

@Path("/like")
@POST
@Secured({User.RoleEnum.ADMIN,User.RoleEnum.MODERATOR,User.RoleEnum.SUBSCRIBER})
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Response like(Comment toUpdate){
    System.out.println("like");
    Comment updated = null;
    try {
        updated = commentService.like(toUpdate);
    } catch (IOException e) {
        // TODO Auto-generated catch block
        return Response
                .serverError()
                .build();
    }
    if(updated !=null){
        return Response
                .ok(updated)
                .build();
    }
    return Response
            .noContent()
            .build();
}

@Secure interface, AuthorizationFilter and AuthenticationFilter are basically the same as the link I posted above.

Angular request

const headersAuth = new Headers({'Content-Type': 'application/json', 'Authorization': 'Bearer ' + this.userService.loggedUserToken});
return this.http.post(this.url + this.likeURL, comment, {headers: headersAuth})
  .map(
    (res: Response) => {
      const body: CommentModel = res.json();
      return body || {};
    }
  )
  .catch(this.handleError);

}

Please have in mind that exactly the same headers and content sent via postman app to the resource, works fine but when I try to send it via Angular POST I get 403 forbidden without even triggering the filters. Also it works when I remove @Secure and Authorization header from Angular

R. Richards
  • 24,603
  • 10
  • 64
  • 64
Milorad
  • 155
  • 14
  • I had this same issue with Angular 2 and Spring Boot. Your browser is going to send a preflight `OPTIONS` request before it sends your `POST`. The `OPTIONS` request is failing authentication because the token is not being added to the `OPTIONS` preflight rqeust headers which results in failed authentication and a 403. You can write a filter to allow all `OPTIONS` request through without authentication, or a filter to check for `OPTIONS` request and return a status 200. Here is a link about the preflight options requesthttps://developer.mozilla.org/en-US/docs/Glossary/Preflight_request – Jason White Jun 26 '17 at 21:30
  • The reason Postman works is because Postman isn't sending a CORS Request and the Angular client is. – Jason White Jun 26 '17 at 21:34
  • It seams logical but how doesn't my filter in CorsFilter class doesn't get executed? It doesn't even get to the authentication point – Milorad Jun 26 '17 at 22:49
  • I resolved my problem by making my url variable private in angular and putting the `http:localhost:..` in my url and it worked like magic – Big Zed Dec 17 '19 at 19:23

1 Answers1

1

After tedious Googling I found the solution. It is utilizing tomcat's cors filters.

Add this to the web.xml

    <filter-name>CorsFilter</filter-name>
    <filter-class>org.apache.catalina.filters.CorsFilter</filter-class>

    <!-- With or without this, it doesn't work -->
    <init-param>
        <param-name>cors.allowed.headers</param-name>
        <param-value>x-rest-version,Content-Type,X-Requested-With,accept,Origin,Access-Control-Request-Method,Access-Control-Request-Headers,Authorization</param-value>
    </init-param>

    <init-param>
        <param-name>cors.allowed.origins</param-name>
        <param-value>http://localhost:4200</param-value>
    </init-param>

    <init-param>
        <param-name>cors.allowed.methods</param-name>
        <param-value>GET,POST,HEAD,OPTIONS,PUT,DELETE</param-value>
    </init-param>

    <init-param>
        <param-name>cors.exposed.headers</param-name>
        <param-value>Location</param-value>
    </init-param>

</filter>
Milorad
  • 155
  • 14