Getting a CORS issue when posting data from frontend(React) - reactjs

I'm posting data from react to spring boot backend using axios. Please refer the below react code.
const handleSubmit = (e) =>{
e.preventDefault()
alert( "Username "+ username + " Password "+password);
axios.post('http://localhost:8080/sign-up',{
username:username,
password:password})
};
Below is my spring code.
#PostMapping("/sign-up")
public String signUp(#RequestBody User user) {
if(userRepository.findByUsername(user.getUsername())==null) {
Set<Role> roles= new HashSet<>();
Role role= roleRepository.findByName("USER");
roles.add(role);
user.setRoles(roles);
user.setPassword(bcryptEncoder.encode(user.getPassword()));
userRepository.save(user);
return "Success";
}
else{
return "Username Already Exsist";
}
}
When I post data from front end i'm getting below response in the browser console.
Access to XMLHttpRequest at 'http://localhost:8080/sign-up' from origin 'http://localhost:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: It does not have HTTP ok status.
xhr.js:178 POST http://localhost:8080/sign-up net::ERR_FAILED
createError.js:16 Uncaught (in promise) Error: Network Error
at createError (createError.js:16)
at XMLHttpRequest.handleError (xhr.js:83)
I've inserted following line in the controller of the spring boot.
#CrossOrigin(origins = "http://localhost:3000")
But the above solution didn't work. I suspect during the post call option call is happening to check the resource and in that case only this cors issue is occuring. Appreciate any input to this issue.

I had a similar issue recently.
I got a first step further by removing spring-boot-starter-security from my POM temporarily, because it was blocking the call giving unauthorized. So I figured out that that was my problem. I had to configure CORS for Spring security and then I got it to work. Apologies for the vague answer, but I hope it can help you look in another direction that could help you.

I have found the solution for the above problem. This cors issue is coming when there is a communication happening between different domains. So I have added below line in the configure() method of SecurityConfig class.
http.cors();
Then I have implemented the following CustomCorsFilter class.
#Component
public class CustomCorsFilter extends OncePerRequestFilter {
#Override
protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
httpServletResponse.setHeader("Access-Control-Allow-Origin", "*");
httpServletResponse.setHeader("Access-Control-Allow-Methods", "GET, POST, PATCH, PUT, DELETE, OPTIONS");
httpServletResponse.setHeader("Access-Control-Max-Age", "3600");
httpServletResponse.setHeader("Access-Control-Allow-Headers", "Origin, Content-Type, Allow, authorization, content-type, xsrf-token");
httpServletResponse.addHeader("Access-Control-Expose-Headers", "xsrf-token");
if ("OPTIONS".equals(httpServletRequest.getMethod())) {
httpServletResponse.setStatus(HttpServletResponse.SC_OK);
} else {
filterChain.doFilter(httpServletRequest, httpServletResponse);
}
}
}
Now there is no cors issue.

Related

React JS send post request to Springboot Rest Server and get error

I am hosting my React app in localhost:3000, and hosting my SpringBoot RESTServer at localhost:8080, tried to send a post request to that server and got following error:
Access to XMLHttpRequest at 'http://localhost:8080/employees' from origin 'http://localhost:3000' 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.
but i already put Access-Control-Allow-Origin in my code:
axios.post('http://localhost:8080/employees', params, {
headers: {
'Access-Control-Allow-Origin': '*',
'Content-Type': 'application/json',
'mode': 'no-cors'
}
}
)
API tested with postman and it worked.
Please help me.
That is caused by the CORS issue. You can google it and acquire lots of explanation which will tell you why the request is blocked when you call an api through the browser.
Basically, the CORS issue of Access-Control-Allow-Origin could be solved by setting you api endpoint. For sprint-boot api, you can try this two way.
Add #CrossOrigin onto the specific path controller
#CrossOrigin(origins = "http://localhost:3000")
#GetMapping("/employees")
public Employees getEmployees() {
// ...
}
Set the global CORS configuration
#Configuration
public class MyConfiguration {
#Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurerAdapter() {
#Override
public void addCorsMappings(CorsRegistry registry) {
// Allow all origin to call your api
registry.addMapping("/**");
}
};
}
}
You can reference this article for more clearly CORS settings in spring-boot.

CSRF/XSRF protection for Spring Security and AngularJS

I tried to add CSRF/XSRF protection to my application, but ran into strange behavior. All get requests work fine, but on all post/put/delete I'm getting 403 Unauthorized. And the strangest thing is that when I tried to debug my CSRF filter, requests do not reach it, they are rejected somewhere earlier. They do not even reach my authentication filter, so I can not figure out what the problem may be.
My security config:
#Override
public void configure(HttpSecurity http) throws Exception {
http
...
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.addFilterBefore(new StatelessAuthenticationFilter(tokenAuthenticationService()), UsernamePasswordAuthenticationFilter.class)
.addFilterAfter(new CsrfHeaderFilter(), CsrfFilter.class)
.csrf().csrfTokenRepository(csrfTokenRepository());
}
private CsrfTokenRepository csrfTokenRepository() {
HttpSessionCsrfTokenRepository repository = new HttpSessionCsrfTokenRepository();
repository.setHeaderName("X-XSRF-TOKEN");
return repository;
}
I do not add the filters since as I said, the requests do not reach them. But if needed I will complete my question. I hope for your help, thank you in advance!
In principle, the CSRF mechanism in Spring stores the CSRF token in a HTTP only cookie. Because JavaScript cannot access a HTTP only cookie, you need to tell spring to disable HTTP only:
.and().csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
Then you can read the cookie from Angular and add it to the XSRF-TOKEN header with each request.
This is the general case. I am not sure if this fits your special case.
Assuming that the rest of your config/filters working properly, you're facing this issue because of this: SessionCreationPolicy.STATELESS.
You can have a look under the hood of Spring CsrfFilter. You'll see that it needs to remember the value of each CSRF-token for each user inside a session, and since you are not using sessions it can't be done.
What to do next - is really up to you. Some people saying that if you app is stateless there is actually no need for CSRF protection. Spring docs saying that CSRF attacks are still relevant. I think it really depends on your authentication mechanism.
You might also want to look at this nice article, for example.
Hope it helps.
Many thanks for the answers, they really helped me to find a solution. And I want to share my solution if in the future someone will face the same issue.
As noted in the answers I used SessionCreationPolicy.STATELESS and did not have sessions so instead of HttpSessionCsrfTokenRepository I had to use CookieCsrfTokenRepository with withHttpOnlyFalse() to allow AngularJS to read cookies.
As a result, I have a configuration like this:
#Override
public void configure(HttpSecurity http) throws Exception {
http
...
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.addFilterBefore(new StatelessAuthenticationFilter(tokenAuthenticationService()), UsernamePasswordAuthenticationFilter.class)
.addFilterAfter(new CsrfHeaderFilter(), CsrfFilter.class)
.csrf().csrfTokenRepository(csrfTokenRepository());
}
If someone is interested in how the CsrfHeaderFilter looks:
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);
}
}
My second problem was CORS. AngularJS documentation says:
"The header will not be set for cross-domain requests."
To solve this problem, I had to use an HTTP Interceptor:
.factory('XsrfInterceptor', function ($cookies) {
return {
request: function (config) {
var headerName = 'X-XSRF-TOKEN';
var cookieName = 'XSRF-TOKEN';
config.headers[headerName] = $cookies.get(cookieName);
return config;
}
};
});
.config(['$httpProvider', function($httpProvider) {
$httpProvider.interceptors.push('XsrfInterceptor');
}]);
I hope my answer will be useful.

How to make POST request to external API using Angular2?

I'm trying to leverage CORS to make a post request. I came across several articles/answers related to CORS but somehow just couldn't get it working.
As I understand, the access-control-allow-origin: * is to be set on the server side to get this working but what I have here is a angular-cli project.
My project is purely Angular 2.1 based and no backend server is involved. Any suggestions as to how to set it up properly will be highly appreciated.
The exact error that I'm getting is this:
"NetworkError: 404 Not Found - https://flowxo.com/hooks/a/rbpja7r2/?usertype=User"
and this warning in console:
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the
remote resource at https://flowxo.com/hooks/a/rbpja7r2/?usertype=User.
(Reason: CORS header 'Access-Control-Allow-Origin' missing).
Update:
Here's how I'm trying to make a POST request:
let headers = new Headers();
headers.append('Access-Control-Allow-Headers', 'Content-Type');
headers.append('Content-Type', 'application/json');
headers.append('Access-Control-Allow-Methods', 'POST, OPTIONS');
headers.append('Access-Control-Allow-Origin', '*');
return this.http.post(
this.flowxoUrl,
JSON.stringify(formData),
{headers: headers}
)
.map((res:Response) => res.json())
.catch((error:any) => Observable.throw(error.json().error || 'Server error')); //...errors if any
This is server side issue, not Angular.
Your server should respond with Access-Control-Allow-Origin header.
Java (Spring) example:
#Component
#Order(Ordered.HIGHEST_PRECEDENCE)
public class SimpleCorsFilter implements Filter {
#Override
public void init(FilterConfig filterConfig) throws ServletException {}
#Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) servletResponse;
HttpServletRequest request = (HttpServletRequest) servletRequest;
response.setHeader("Access-Control-Allow-Origin", "*"); //you can specify domains here * - is a wildcard, it will allow all origins to request
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE, PUT");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "x-requested-with, authorization, content-type");
if("OPTIONS".equalsIgnoreCase(request.getMethod())) {
response.setStatus(HttpServletResponse.SC_OK);
} else {
filterChain.doFilter(servletRequest, servletResponse);
}
}
#Override
public void destroy() {}
}

HTTP POST turns into OPTIONS AngularJS

I'm trying to submit a login form to a rest Api, translating my Jquery/Javascript code into AngularJS. I try to use $http service for send the request, but when I submit the form, the POST request turns into OPTIONS and no request params are passed to. This is my code:
controller.formData = {
username : $scope.formData.username,
password : $scope.formData.password,
};
$http({
method : 'POST',
url : 'http://localhost:8080/multe-web/signin',
data : controller.formData,
headers : { 'Content-Type': 'application/json' }
})
.success(function(data) {
console.log(data);
});
This is a screenshoot of browser's console
Why no parameters are passed to the HTTP POST request?
Can someone help me?
If you are trying to do a Cross Origin Request, this could be a 'preflight request':
See this post: How to disable OPTIONS request?
This request is called "preflight request". This appends when you try to access a resource that is from another domain. For more information you can search for Cross-origin resource sharing (CORS). During the preflight request, you should see headers starting with Access-Control-Request-* These request headers are asking the server for permissions to make the actual request. Your preflight response needs to acknowledge these headers in order for the actual request to work.
The OPTIONS that you see is a pre-flighted request that is initiated by the browser. This happens due to CORS that stands for "CROSS ORIGIN RESOURCE SHARING`.
Pre-flighted requests
In your case, your backend server needs to acknowledge the OPTIONS request and send a 200 back. Only then the browser fires the real POST request
Here are some links to get you started
Same Origin policy
AngularJS and CORS
Similar question on SO
You must define a CORS filter on your backend.
I don't know which language you are using but an example in spring framework (java) would be the following.
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
#Component
#Order(Ordered.HIGHEST_PRECEDENCE)
public class SimpleCorsFilter implements Filter {
#Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) res;
HttpServletRequest request = (HttpServletRequest) req;
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE, PUT");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "x-requested-with, authorization, cache-control, content-type, Origin, key");
if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
response.setStatus(HttpServletResponse.SC_OK);
} else {
chain.doFilter(req, res);
}
}
#Override
public void init(FilterConfig filterConfig) {
}
#Override
public void destroy() {
}
}
Basically you are stating that response.setHeader("Access-Control-Allow-Origin", "*"); can come from any domain. ( in a production environment you would limit this offcourse to your known domains ).
When implementing this filter your options request will pass through without problems.
The options call is something the browser does automatically and you really don't want to disable this as some other answers are suggesting.
Kind regards

AngularJS: X-Auth-Token not sent (error 401)

I'm here because I tried everything and I don't understand what I'm doing wrong.
As a backend, I use Spring MVC, Rest and Spring Security. I'm using a custom authentication's system based on tokens (entirely stateless). I've also a CORS's filter, because my front app is not running on the same server.
So here's what I did in angularJS, in order to get authenticate when I want to access a protected resource:
I've created an interceptor that change each request to add the token found from a cookie
I add a custom header on the request
Here's what my interceptor do:
request: function(config) {
var token = $cookieStore.get("token");
if (token && token.key) {
config.headers["X-Auth-Token"]=token.key;
}
console.log(config.headers);
return config || $q.when(config);
}
But when I try to access a protected resource, even if the token is still valid, it never puts the X-Auth-Token headers on the request! It tried to send an option's request and failed with a 401 error!
But if I do the same with a url parameter (like token=....), the interceptor works as expected...(I've implemented both system : by parameter and by header)
What I don't understand is why the header is not put as expected?? and why it works perfectly with something like POSTMAN for instance?
Please help, I'm losing my hairs with this...
It's probably cleaner to do this instead of your accepted answer.
if (request.getMethod().equals("OPTIONS")) {
response.setStatus(HttpServletResponse.SC_OK);
} else {
chain.doFilter(request, response);
}
I fixed the problem by changing my filter like this:
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
LOG.info("goes on the filter");
HttpServletResponse response = (HttpServletResponse) res;
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "POST, GET, DELETE, PUT");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "Accept, Content-Type, Origin, Authorization, X-Auth-Token");
response.addHeader("Access-Control-Expose-Headers", "X-Auth-Token");
if (request.getMethod().equals("OPTIONS")) {
try {
response.getWriter().print("OK");
response.getWriter().flush();
} catch (IOException e) {
LOG.error(e.getMessage());
}
} else {
chain.doFilter(request, response);
}
}
Not sure if this will solve your issue, but $cookieStore can only get/set strings, so the following:
var token = $cookieStore.get("token");
if (token && token.key) {
Will never evaluate to true.

Resources