How to Solve 403 Error in Spring Boot Post Request

SpringRestMavenSpring Boot

Spring 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.

enter image description here

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:

  1. Requests done from postman are different to the one done from mobile (uri, method, headers)
  2. Invalid token
  3. CORS (read something about it, google is full of articles) add @CrossOrigin annotation to your controller.
  4. 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.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionHarshal DeshmukhView Question on Stackoverflow
Solution 1 - SpringEzzat EissaView Answer on Stackoverflow
Solution 2 - SpringdesossView Answer on Stackoverflow
Solution 3 - SpringBaba FakruddinView Answer on Stackoverflow
Solution 4 - SpringKhamaseenView Answer on Stackoverflow
Solution 5 - SpringChristopherView Answer on Stackoverflow
Solution 6 - SpringStephen PaulView Answer on Stackoverflow
Solution 7 - SpringOjonugwa Jude OchalifuView Answer on Stackoverflow