How to call Rest API Post method in Angular 8. Getting CORS error - spring

I am trying to call Rest API post method in Angular 8.
'Access-Control-Allow-Origin' :'*','Access-Control-Allow-Methods' :'POST, GET, PUT, OPTIONS, DELETE, PATCH','Access-Control-Max-Age' :'3600','Access-Control-Allow-Headers' :'Origin, X-Requested-With, Content-Type, Accept',
'Content-Type': 'application/json',
'username':'dexadmin','password':'dexcenter'}
this.http.post('http://localhost:8080/DEXRESTAPI/queues/getMaxJob',{},{​​'headers':headers}).
subscribe((data:any) => {​​
console.log('Data', +data);
console.log('Was my name');
}​​);
}
Rest API is as below:
#RequestMapping(value = "/getMaxJob", method = RequestMethod.POST)
public Integer getMaxJob() throws JmsException {
try {
return queueDelegate.getMaxJob();
}
catch (Exception cause) {
logger.error("Error getting MaxJob" + cause.getMessage(), cause);
throw new JmsException(cause.getLocalizedMessage());
}
}
I am getting 401 unauthorized error. I am getting on consol below error -
Access to XMLHttpRequest at 'http://localhost:8080/DEXRESTAPI/queues/getMaxJob/' from origin 'http://localhost:4200' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
I have set in SecurityConfig.java file
http
.httpBasic().disable()
.csrf().disable()
.anonymous().disable()
.authorizeRequests()
.antMatchers("/oauth/token").permitAll()
.antMatchers(HttpMethod.OPTIONS).permitAll()
.antMatchers(HttpMethod.GET).permitAll()
.antMatchers(HttpMethod.POST).permitAll()
.anyRequest().fullyAuthenticated();
}

Related

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.

Spring RestTemplate + Basic Authentication + Post with request Body: 500 Internal Server Error

I am looking for a working approach for Rest Client using Spring (5.x) RestTemplate with Basic Authentication + passing Request Body as HTTP Post.
NOTE: the service works fine If I hit request using postman/ other rest client, instead of a java client/ test class.
I am getting 500 Internal Server Error
org.springframework.web.client.HttpClientErrorException: 500 Internal Server Error
at org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:94)
at org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:79)
at org.springframework.web.client.ResponseErrorHandler.handleError(ResponseErrorHandler.java:63)
at org.springframework.web.client.RestTemplate.handleResponse(RestTemplate.java:777)
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:730)
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:704)
at org.springframework.web.client.RestTemplate.postForObject(RestTemplate.java:459)
at com.xxx.xxx.xxx.utils.Util.updateFlag(Util.java:125)
at com.xxx.xxx.xxx.utils.UtilsImplTest.testUpdateFlag(UtilsImplTest.java:122)
My Test class:
#Test
public void testUpdateFlag() {
Request request = new Request();
request.setUserId("aa");
request.setFlag("Y");
request.setValue("D");
Response response = null;
try {
response = util.updateFlag(request);
} catch (JsonProcessingException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
assertNotNull(response);
}
My Implementation util class: where I am setting Basic Authorization in header.
#Autowired private RestTemplate restTemplate;
private HttpHeaders getHeaders(){
HttpHeaders headers = new HttpHeaders();
headers.add("Authorization", "Basic " + <base64_encrypted_password>);//500
// headers.setContentType(MediaType.APPLICATION_JSON); //401
return headers;
}
public Response updateFlag(Request request) throws JsonProcessingException, URISyntaxException {
restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
HttpEntity<Request> requestEntity = new HttpEntity<>(request, getHeaders());
Response response = restTemplate.postForObject(url, requestEntity, Response.class);
return response;
}
If I comment out the basic authorization line in getHeaders() method, then it throws 401 Unauthorized, which is fairly logical.
org.springframework.web.client.HttpClientErrorException: 401 Unauthorized
at org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:94)
at org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:79)
at org.springframework.web.client.ResponseErrorHandler.handleError(ResponseErrorHandler.java:63)
at org.springframework.web.client.RestTemplate.handleResponse(RestTemplate.java:777)
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:730)
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:704)
at org.springframework.web.client.RestTemplate.postForObject(RestTemplate.java:459)
at com.xxx.xxx.xxx.utils.Util.updateFlag(Util.java:125)
at com.xxx.xxx.xxx.utils.UtilsImplTest.testUpdateFlag(UtilsImplTest.java:122)
I have tried almost every option suggested over stackoverflow in similar content, unable to identify the exact root cause why setting authorization in header doesn't validate & throws 500 Internal Server Error.
I have spent quite a handful time investigating, with no luck. Appreciate any pointers.

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.

ClientAbortException when executing angular 2 post in tomcat

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();
}

Resources