Home > front end >  Spring boot @RestController fails with 403 error after adding authentication
Spring boot @RestController fails with 403 error after adding authentication

Time:11-11

I am using the Spring – REST Controller tutorial which works just as advertized. I then added a JavaScript ajax snipped which sends a POST request that works too.

After that I added authentication to our ActiveDirectory server. The authentication itself works, but all POST request are now rejected with a 403 error at the web browser console. My controller method is never called.

Surprisingly all GET requests still work, both by modifying the ajax snippet to use the GET methods, or by manually entering the url in the web browser. Also standard html forms using POST requests work too (however they are using @Controller instead of @RestController annotations).

So it seems to me that the JavaScript ajax code is missing some essential headers, or maybe the Spring security configuration blocks such request intentionally. Please advise how to fix this problem.

Java code:

@RestController
public class Controller {

    @PostMapping({"/rest/api/1/command"})
    public void command(@RequestBody String s) {
        System.out.println("we got: "   s);
    }
    ...
}

JavaScript Code:

<script>
$.ajax({
    async: false,
    type: "POST",
    url: "/rest/api/1/command",
    contentType: "application/json",
    dataType: "json",
    data: { command: "run" },
    success: function (data) {
        return true;
    }
});
</script>

Authentication Java code:

package com.example.demo;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.ldap.authentication.ad.ActiveDirectoryLdapAuthenticationProvider;

@Configuration
public class ActiveDirectoryConfig extends WebSecurityConfigurerAdapter {

    @Value("${activedirectory.domain}")
    private String domain;
    @Value("${activedirectory.url}")
    private String url;
    @Value("${activedirectory.rootdn}")
    private String rootdn;
    @Value("${activedirectory.searchfilter}")
    private String searchfilter;
    @Value("${admin.username:}")
    private String adminusername;
    @Value("${admin.password:}")
    private String adminpassword;

    @Override
    public void configure(AuthenticationManagerBuilder auth) throws Exception
    {
        ActiveDirectoryLdapAuthenticationProvider adProvider =
                new ActiveDirectoryLdapAuthenticationProvider(
                        domain, url, rootdn);
        adProvider.setConvertSubErrorCodesToExceptions(true);
        adProvider.setUseAuthenticationRequestCredentials(true);
        if (searchfilter != null)
            adProvider.setSearchFilter(searchfilter);
        auth.authenticationProvider(adProvider);
        auth.eraseCredentials(false);
    }
}

CodePudding user response:

After Marcus Hert da Coregio's comment I was able to fix the program by adding the CSRF token to the ajax call. The actual value of the CSRF token will be provided by the Thymeleaf template.

Code:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta id="_csrf" name="_csrf" th:content="${_csrf.token}"/>
    <meta id="_csrf_header" name="_csrf_header" th:content="${_csrf.headerName}"/>
    <title>Title</title>
    <script src="https://code.jquery.com/jquery-3.3.1.min.js"
            integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8="
            crossorigin="anonymous">
    </script>
</head>
<body>
<script>
var token = $('#_csrf').attr('content');
var header = $('#_csrf_header').attr('content');
$.ajax({
            async: false,
            type: "POST",
            url: "/rest/api/1/command",
            contentType: "application/json",
            dataType: "json",
            data: { command: "run" },
            beforeSend: function(xhr) {
                xhr.setRequestHeader(header, token);
            },
            success: function (data) {
                return true;
            }
        });
</script>
<p>hello world</p>
</body>
</html>
  • Related