ClientAbortException when executing angular 2 post in tomcat - tomcat7

After completing the quickstart of angular 2 app i tried to execute a post to a rest web service deployed in another server, which is tomcat.
I have added an OPTIONS method to allow all origins. After returning from the getOptions() method it enters the getTestResponse method and a
ClientAbortException occurs.
Sample code :
return this._http.post(url, body, options)
.map(res => res.json());
Web service code :
#OPTIONS
#Path("samplePath")
public Response getOptions() {
return Response.ok()
.header("Access-Control-Allow-Origin", "*")
.header("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE, PUT")
.header("Access-Control-Allow-Headers", "X-Requested-With, Content-Type, Origin, Authorization, Accept, Client-Security-Token, Accept-Encoding")
.build();
}
#POST
#Consumes({MediaType.APPLICATION_JSON})
#Produces({MediaType.APPLICATION_JSON})
public TestResponse getTestResponse(TestRequest testRequest) {
//somehow get response from database
//
}
Do you have any idea why this exception could occur?

It's because here you add headers just to your option request.
But actually POST is failing.
To fix issue, best is to add a CORS filter to your application which will be applied to all requests.
CorsFilterApi
import org.jboss.resteasy.plugins.interceptors.CorsFilter;
#ApplicationPath(RestApplication.ROOT_PATH)
public class RestApplication extends Application {
public static final String ROOT_PATH = "/resources";
private Set<Object> singletons = new HashSet<>();
...
#Override
public Set<Object> getSingletons() {
CorsFilter corsFilter = new CorsFilter();
corsFilter.getAllowedOrigins().add("*");
corsFilter.setAllowCredentials(true);
corsFilter.setAllowedHeaders("origin, content-type, accept, authorization");
corsFilter.setAllowedMethods("GET, POST, DELETE, PUT, OPTIONS, HEAD");
singletons.add(corsFilter);
return singletons;
}
}
EDIT:
Solution 2 apply headers just to your post request and
make sure that your #OPTION REQUEST IS NOT CONTAINING #PATH
because then it's not same request in your case:
#OPTIONS
public Response getOptions() {
return Response.ok()
.header("Access-Control-Allow-Origin", "*")
.header("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE, PUT")
.header("Access-Control-Allow-Headers", "X-Requested-With, Content-Type, Origin, Authorization, Accept, Client-Security-Token, Accept-Encoding")
.build();
}
#POST
#Consumes({MediaType.APPLICATION_JSON})
#Produces({MediaType.APPLICATION_JSON})
public Response getTestResponse(TestRequest testRequest) {
return Response.ok()
.header("Access-Control-Allow-Origin", "*")
.header("Access-Control-Allow-Headers", "origin, content-type, accept, authorization")
.header("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT, OPTIONS").build();
}

Related

NullPointerException in filter

I'm running a filter with every request where I check for "X-Company-ID" header. The header is only sent on login reqest and afterwards the CompanyContext currentCompany is set from the current user data.
Here is my filter, I added some print lines to understand what's going on:
#Component
public class CORSFilter extends GenericFilterBean {
private CompanyService companyService;
#Autowired
public CORSFilter(CompanyService companyService) {
this.companyService = companyService;
}
public CORSFilter() {
}
#Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin"));
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setHeader("Access-Control-Allow-Methods", "POST, PUT, PATCH, GET, OPTIONS, DELETE");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "origin, authorization, accept, content-type, content-disposition," +
"x-requested-with, remember-me, Pragma, Cache-Control, If-Modified-Since, X-Auth-Token, X-Company-ID, X-Client-Certificate");
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setHeader("Access-Control-Expose-Headers", "X-Auth-Token, X-Company-ID, Content-Disposition");
String companyHeader = request.getHeader("X-Company-ID");
System.out.println("Company header: " + companyHeader);
if (companyHeader != null && !ObjectUtils.isEmpty(companyHeader)) {
System.out.println("Company header 2: " + companyHeader);
Company company = companyService.getCompanyByDomainIdentifier(companyHeader);
if (!ObjectUtils.isEmpty(company) || company != null) {
System.out.println(company.getCompanyName());
CompanyContext.setCurrentCompany(company.getId());
}
}
if (!request.getMethod().equalsIgnoreCase("OPTIONS")) {
chain.doFilter(req, res);
}
}
}
The output goes as follows:
Company header: 2
Company header 2: 2
Avantirent OÜ
Company header: 2
Company header 2: 2
2020-02-05 16:17:29.930 ERROR 6124 --- [nio-8080-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] threw exception
java.lang.NullPointerException: null
at com.bitweb.syda.config.filter.CORSFilter.doFilter(CORSFilter.java:52)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:215)
If I understand correctly- the first time the filter runs it gets the header and the right company. The second time it gets the header but getting the company gives me a NullPointerException.
The exception comes from the line Company company = companyService.getCompanyByDomainIdentifier(companyHeader); which is either a correct Company or null. I don't do anything with it if it's null, but it still gives me a NullPointerException.
I'm really stuck here, any help?

How to handle CORS error with an angular app and a java backend?

I have an angular App that tries to send ajax requests to a java backend (API built with Jersey).
Here is the client request :
let settings = {
"url": "http://localhost:8080/simulator/config",
"method": "POST",
"timeout": 0,
"headers": {
"Content-Type": "application/json"
},
"data": JSON.stringify({
"fromDate": fromDate,
"tsIntervalTag": tsIntervalTag,
"tsIntervalDevice": tsIntervalDevice,
"devicePerMinute": devicePerMinute,
"tagPerMinute": tagPerMinute,
"quantityOfTags": quantityOfTags,
"quantityOfDevices": quantityOfDevices
}),
};
$.ajax(settings).done(function (response) {
console.log(response);
});
And here is the java backend request handler:
#POST
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public Response postIt(String body) {
try {
//do stuff
return Response.ok(body, MediaType.APPLICATION_JSON).header("Access-Control-Allow-Origin", "*").build();
} catch (Exception e) {
return Response.serverError().entity(e).header("Access-Control-Allow-Origin", "*").build();
}
}
As you can see, as nearly all answers about CORS state, the Access-Control-Allow-Origin header is set to * wildcard, so from my understanding, the response should get back to the browser.
But the browser console returns the following:
Cross-Origin Request Blocked: The Same Origin Policy disallows reading
the remote resource at http://localhost:8080/simulator/config.
(Reason: CORS header ‘Access-Control-Allow-Origin’ missing).
Cross-Origin Request Blocked: The Same Origin Policy disallows reading
the remote resource at http://localhost:8080/simulator/config.
(Reason: CORS request did not succeed).
So what did I miss ?
It seems like with Jersey, it is better to use filters to set CORS. (At least in my scenario where all requests should be accepted from anywhere (so far)).
So I created the following class (in its own java file):
package com.example;
import java.io.IOException;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerResponseContext;
import javax.ws.rs.container.ContainerResponseFilter;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.ext.Provider;
#Provider
public class CORSFilter implements ContainerResponseFilter {
#Override
public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext)
throws IOException {
System.out.println("FILTER HERE ");
MultivaluedMap<String, Object> headers = responseContext.getHeaders();
headers.add("Access-Control-Allow-Origin", "*"); // Allow Access from everywhere
headers.add("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT");
headers.add("Access-Control-Allow-Headers", "X-Requested-With, Content-Type");
}
}
And that's is basically it. All requests will go through this and get the correct headers. I do not know why setting the headers in method directly did not work though.

CORS Preflight request - Custom auth token in header - Spring and Angular 4

I'm having trouble requesting a simple GET on my local server with a token as a header.
My browser keep sending preflight request on simple GET request.
I tried with postman / curl and i don't have any issue.
Here's the actual code :
Server (Spring) :
#Override
public void doFilter(final ServletRequest req, final ServletResponse res,
final FilterChain chain) throws IOException, ServletException {
final HttpServletResponse response = (HttpServletResponse) res;
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods",
"POST, GET, PUT, OPTIONS, DELETE, PATCH");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "x-requested-with, x-auth-token, Content-Type");
response.setHeader("Access-Control-Expose-Headers", "x-auth-token");
response.setHeader("Access-Control-Allow-Credentials", "true");
final HttpServletRequest request = (HttpServletRequest) req;
if (request.getMethod().equals("OPTIONS")) {
try {
response.getWriter().print("OK");
response.getWriter().flush();
} catch (IOException e) {
e.printStackTrace();
}
} else {
chain.doFilter(request, response);
}
}
So my x-auth-token here is a jwt-like token.
In my Angular code, I simply add my token as a x-auth-token like this :
getAll() {
return this.http.get('http://127.0.0.1:8080/module', this.jwt()).map((response: Response) => response.json());
}
private jwt() {
// create authorization header with jwt token
let token = localStorage.getItem('user');
let obj = JSON.parse(token)
console.log(obj.token.token)
if (obj.token) {
let headers = new Headers({
'x-auth-token': obj.token.token,
'Content-Type': "application/json"
});
return new RequestOptions({ headers: headers });
}
}
Note that my authentication / signup routes work fine, i'm only having trouble communicating my x-auth-token header to my Spring server.
Both my webserver are running locally : Spring on 8080 and Angular on 8000.
Any help would be greatly appreciated,
Thank you,
First, postman and curl are not good ways to test for CORS, since they don't enforce the same origin policies as standard browsers do. They will work even in cases that will fail in the browser.
Second, the reason your request is pre-flighted by the browser is that it is not a simple request, because of the x-auth-token header and the application/json Content-Type. Only application/x-www-form-urlencoded
multipart/form-data and text/plain content-types are allowed in simple requests.
I would assume that the problem is in the OPTION response. Make sure that it includes all the relevant headers. It might be possible it is failing or that you are loosing the added headers because you are handling it differently than other responses.

Angular2 JWT Authorization header missing in http response (CORS support added)

I'm trying to do an authorization using JWT (Json Web Token). On front-end I'm using Angular2, on back-end Spring REST api. I've added CORS filter on back-end side.
In http request I'm sending username and password and expecting token in 'Authorization' header of response. Everything works fine when I use Postman, I receive all headers including 'Authorization'.
Also, when I record the traffic in Chrome Console (while doing user login through the form) 'Authorization' header is present in response, so obviously it returns back to the browser. But when I list headers in my angular app, there is just few headers in array:
// auth.service.ts
login(username, password): Observable<boolean> {
// call remote service to authenticate user
return this.http.post(this.authUrl, JSON.stringify({ username: username, password: password }))
.map((response: Response) => {
console.log("Authorization header: " + response.headers.get('Authorization'));
console.log("all headers: " + response.headers.keys());
// TODO - login successful if there's a jwt token in the response
});
}
Result of those 2 console output is:
Authorization header: null
all headers: Pragma,Cache-Control,Expires
Here is the screenshot from Google Chrome console where you can see that all needed headers are present on client side:
Server-side token generation (Spring Boot):
public void addAuthentication(HttpServletResponse response, String username) throws UnsupportedEncodingException {
// Token generation
String JWT = Jwts.builder()
.setSubject(username)
.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
.signWith(SignatureAlgorithm.HS512, secret.getBytes("UTF-8"))
.compact();
response.addHeader(headerString, tokenPrefix + " " + JWT);
}
Does anyone has some useful advice?
Thanks
SOLUTION:
public class CORSFilter implements Filter{
#Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) res;
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");
response.setHeader("Access-Control-Expose-Headers", "Authorization");
chain.doFilter(req, response);
}
#Override
public void init(FilterConfig arg0) throws ServletException {
// TODO Auto-generated method stub
}
#Override
public void destroy() {
// TODO Auto-generated method stub
}
}
Authorization header is present but since it's a custom header the browser will not allow you to access it directly even though chrome will see it.
To solve that you need one more important header in you cors config.
Access-Control-Expose-Headers: Authorization, CustomHeader2, e.tc
Make sure to expose any other custom headers that you want your app to have access to.

Jersey not triggering ContainerRequestFilter

I'm trying to use a ContainerRequestFilter to enforce some authentication on a Grizzly based Jersey application.
I create my own resource config by extending PackagesResourceConfig:
public class MainResourceConfig extends PackagesResourceConfig {
public MainResourceConfig() {
super("za.co.quinn.ws");
Map<String, Object> properties = getProperties();
properties.put(
"com.sun.jersey.spi.container.ContainerRequestFilter",
"com.sun.jersey.api.container.filter.LoggingFilter;" + MainRequestFilter.class.getName()
);
properties.put(
"com.sun.jersey.spi.container.ContainerResponseFilters",
"com.sun.jersey.api.container.filter.LoggingFilter;" + MainResponseFilter.class.getName()
);
}
}
The request filter is for authentication:
#Inject
Authorization authorization;
#Override
public ContainerRequest filter(ContainerRequest request) {
if (!request.getRequestUri().getPath().endsWith(".wadl"))
authorization.authorize(request);
return request;
}
The response filter is for headers:
#Override
public ContainerResponse filter(ContainerRequest request, ContainerResponse response) {
response.getHttpHeaders().add("Access-Control-Allow-Origin", "*");
response.getHttpHeaders().add("Access-Control-Allow-Methods", "GET, HEAD, POST, DELETE, PUT");
response.getHttpHeaders().add("Access-Control-Allow-Headers", "Authorization, Content-Type");
return response;
}
The MainResponseFilter does get triggered but not the ContainerRequestFilter.
"The MainResponseFilter does get triggered but not the ContainerRequestFilter."
Look at your properties, mainly the send of it, and compare them
...container.ContainerRequestFilter"
...container.ContainerResponseFilters"
You're missing an s for the request filters. To avoid misspellings like this, you can use constants
ResourceConfig.PROPERTY_CONTAINER_REQUEST_FILTERS
ResourceConfig.PROPERTY_CONTAINER_RESPONSE_FILTERS
You could also do
getContainerRequestFilters().add(new YourRequestFilter());
getContainerResponseFilters().add(new YourResponseFilter());

Resources