Reputation: 1897
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.
Upvotes: 121
Views: 200603
Reputation: 621
This is for those that like me came here because they were writing post tests and encountered 403 instead of any expected errors (depending on code state).
Instead of disabling csrf one can obtain the CSRF token so that you can use it in the header of the call, much like how it works with a form in Thymeleaf.
With tests you just add with(csrf())
to the call like here
mvc.perform(post("/")
.with(csrf()) //CSRF token is sent
.contentType("application/json")
.content(""))
.andExpect(status().isCreated());
This way you can move ahead without disabling CSRF, much like how it likely will work in the production code. There are also other options as you can read in the Testing with CSRF Protection documentation.
If you later need to make this present also in your actual Thymeleaf form you can add csrf to it as a hidden type:
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}" />
Upvotes: 1
Reputation: 10587
This answer is related to this question if you are deploying to Open/WAS Liberty server.
If so, you might get 403 error even though your code works perfectly fine if deploying to embedded Tomcat that comes with Spring boot.
Liberty does not read (or considers) your
server.servlet.context-path=/myapi/v1
that you set in your application.properties or application.yml file for some reason. Or, it just overwrites it, not sure. So, the above context-path
will work just fine if deployment in Spring Boot embeded Tomcat container.
However, when you deploy it to OpenLiberty/WASLiberty, you might find that your endpoints will stop working and you get 403 and/or 404 errors.
In my example, I have api where I have /auth
endpoint in my WebSecurityConfiguration class:
//Customize the /login url to overwrite the Spring default provided /login url.
private AuthenticationFilter authenticationFilter() throws Exception {
final AuthenticationFilter filter = new AuthenticationFilter(authenticationManager());
// This works fine on embedded tomcat, but not in Liberty where it returns 403.
// To fix, in server.xml <appllication> block, add
// <application context-root="/myapi/v1" ... and then both
// auth and other endpoints will work fine in Liberty.
filter.setFilterProcessesUrl("/auth");
// This is temporary "fix" that creates rather more issues, as it
// works fine with Tomcat but fails in Liberty and all other
// endpoints still return 404
//filter.setFilterProcessesUrl("/v1/auth");
return filter;
}
Based on the above context-path
, on Tomcat, it becomes /myapi/v1/auth
while on Liberty, it ends up being just /myapi/auth
which is wrong. I think what Liberty does, it will just take the name of the api and add to it the endpoint, therefore ignoring the versioning.
As a result of this, AntPathRequestMatcher
class matches()
method will result in a non-matching /auth
end point and you will get 403 error. And the other endpoints will result in 404 error.
SOLUTION
In your application.properties, leave
server.servlet.context-path=/myapi/v1
, this will be picked up by embedded Tomcat and your app will continue to work as expected.
In your server.xml configuration for Open/WAS Liberty, add matching context-root to the section like:
<application context-root="/myapi/v1" id="myapi" location="location\of\your\myapi-0.0.1.war" name="myapi" type="war">
, this will be picked up by Open/WASLiberty and your app will continue to work as expected on Liberty container as well.
Upvotes: 1
Reputation: 374
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
}
Upvotes: 8
Reputation: 481
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.
Upvotes: 6
Reputation: 189
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();
}
}
Upvotes: 12
Reputation: 39035
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
...
}
Upvotes: 2
Reputation: 2654
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;
}
}
Upvotes: 263
Reputation: 622
Possible causes:
Upvotes: 8