How to Solve 403 Error in Spring Boot Post Request
SpringRestMavenSpring BootSpring Problem Overview
I am newbie in spring boot rest services. I have developed some rest api in spring boot using maven project.
I have successfully developed Get and Post Api. My GET Method working properly in postman and mobile. when i am trying to hit post method from postman its working properly but from mobile its gives 403 forbidden error.
This is my Configuration:
spring.datasource.url = jdbc:mysql://localhost/sampledb?useSSL=false
spring.datasource.username = te
spring.datasource.password = test
spring.jpa.properties.hibernate.dialect= org.hibernate.dialect.MySQL5InnoDBDialect
Hibernate ddl auto (create, create-drop, validate, update)
spring.jpa.hibernate.ddl-auto = update
Please Suggest me how to solve error.
Spring Solutions
Solution 1 - Spring
you have to disable csrf Protection because it is enabled by default in spring security: here you can see code that allow cors origin.
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter{
@Override
protected void configure(HttpSecurity http) throws Exception{
http.cors().and().csrf().disable();
}
@Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("*"));
configuration.setAllowedMethods(Arrays.asList("*"));
configuration.setAllowedHeaders(Arrays.asList("*"));
configuration.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
}
Solution 2 - Spring
Possible causes:
- Requests done from postman are different to the one done from mobile (uri, method, headers)
- Invalid token
- CORS (read something about it, google is full of articles) add @CrossOrigin annotation to your controller.
- mobile app is doing an OPTION request before performing the POST, and you block OPTION requests. If also from postman the OPTION requests are blocked, add the property spring.mvc.dispatch-options-request=true. Moreover, in case you are using spring security, you have to explicitly allow OPTION requests also for it.
Solution 3 - Spring
In Spring Security Cross-site check is by default enable, we need to disable it by creating a separate class to stop cross-checking.
package com.baba.jaxws;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter{
@Override
//we have stopped the csrf to make post method work
protected void configure(HttpSecurity http) throws Exception{
http.cors().and().csrf().disable();
}
}
Solution 4 - Spring
CSRF is enabled by default in Spring Security. Having this enabled ensures a 403 error on HTTP requests that would change (object) states. For more information please visit: https://docs.spring.io/spring-security/site/docs/3.2.x/reference/htmlsingle/html5/#csrf
It is possible to disable CSRF in the Spring Security. However, it is enabled by default (convention over configuration) and for a good reason. This is also explained in the link provided to Spring's Security.
A working example, using Thymeleaf, might be:
HTML
<head>
<meta name="_csrf" th:content="${_csrf.token}"/>
<meta name="_csrf_header" th:content="${_csrf.headerName}"/>
</head>
JS
function postExample() {
let token = $("meta[name='_csrf']").attr("content");
let header = $("meta[name='_csrf_header']").attr("content");
let data = {username: "", password: "", firstname: "", lastname: ""};
// Object key string interpolation by {[header]:token} works with ES6
fetch(window.location+"/addnote", {
method:"POST",
headers: {
[header]: token,
"charset": "UTF-8",
"Content-Type": "application/json"
},
body: JSON.stringify(data)
}).then(res => console.log(res)).catch(err => console.log(err))
}
CONTROLLER per request of @mahmoud-magdy
@PostMapping("/addnote")
public Long addNote(@RequestBody() String data) {
Gson gson = new Gson();
JSONAddNote json = gson.fromJson(data, JSONAddNote.class);
return <service>.addNote(json.username, json....);
}
class JSONAddNote {
public String username;
public String ...etc
}
Or a more direct CONTROLLER:
@PostMapping("/addnote")
public Long addNote(@RequestBody Data data) {
return <service>.addNote(data);
}
class Data {
public String username;
public String ...etc
}
Solution 5 - Spring
I was able to solve this by using:
<form th:action="@{url}" method="post">
Instead of:
<form action="url" method="post">
It seems the th:action tag does url rewriting to enable csrf validation.
Solution 6 - Spring
To build on the accepted answer
Many HTTP client libraries (eg Axios) implicitly set a Content-Type: JSON
header for POST requests. In my case, I forgot to allow that header causing only POSTS to fail.
@Bean
CorsConfigurationSource corsConfigurationSource() {
...
configuration.addAllowedHeader("Content-Type"); // <- ALLOW THIS HEADER
...
}
Solution 7 - Spring
After hours of trying to find the cause of this sudden problem, I discovered that the annotation I had added earlier
@JsonIgnore
to the password field of my UserAccount
class.
@JsonIgnore
String password
was causing every login request to fail as the password field was coming in as Null from PostMan and my angular application.
Removing JsonIgnore did the trick.
You just have to find another way to hide your password.