Authentication handling in Spring boot 2.x with WebSecurityConfigurerAdapter

Viewed 450

I am using spring boot 2.0.5 with below dependency for spring security of 5.0.8

<dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-security</artifactId>
 </dependency>

I throwing a exception from the rest controller as below for test purpose.

@GetMapping("/greeting")
public Greeting greeting(@RequestParam(value = "name", defaultValue = "World") String name) {
        throw new org.springframework.security.authentication.BadCredentialsException("yoyo");
}

This is how the config class looks like,

    @Configuration    
    @EnableWebSecurity
    public class BasicConfiguration extends WebSecurityConfigurerAdapter {
    
        @Override
        public void configure(WebSecurity web) throws Exception {
            web.ignoring().antMatchers("/error");
    
        }
    
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.csrf().disable().authorizeRequests().antMatchers("/error").permitAll();
        }
    }

I have added /error since spring boot documentation it says,

Spring Boot 2.0 doesn’t deviate too much from Spring Security’s defaults, as a result of which some of the endpoints that bypassed Spring Security in Spring Boot 1.5 are now secure by default. These include the error endpoint and paths to static resources such as /css/, /js/, /images/, /webjars/, /**/favicon.ico. If you want to open these up, you need to explicitly configure that.

Add below in spring boot application class as well

@SpringBootApplication(exclude = {SecurityAutoConfiguration.class })

When i hit the rest end point I get,

{
    "timestamp": "2021-01-17T03:52:50.069+0000",
    "status": 403,
    "error": "Forbidden",
    "message": "Access Denied",
    "path": "/greeting"
}

But I expect status 401 with "message": "yoyo" like below,

{
    "timestamp": 2021-01-17T03:52:50.069+0000,
    "status": 401,
    "error": "Unauthorized",
    "message": "yoyo",
    "path": "/greeting"
}

What changes I need to do for get that response

2 Answers

Add http.exceptionHandling().authenticationEntryPoint(..) to get Unauthorized instead of Forbidden for error field.

@Configuration
@EnableWebSecurity
public class BasicConfiguration extends WebSecurityConfigurerAdapter {
    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/error");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable().authorizeRequests().antMatchers("/error").permitAll();

        http.exceptionHandling().authenticationEntryPoint((request, response, ex) -> {
            // You can add more logic to return different error codes
            response.sendError(401, "Unauthorized");
        });
    }
}

Define a new class that returns ex.getMessage() instead of Access Denied for message field.

@Component
public class CustomErrorAttributes extends DefaultErrorAttributes {
    @Override
    protected String getMessage(WebRequest webRequest, Throwable error) {
        return error.getMessage();
    }
}

Output:

{
    "timestamp": "2021-01-20T15:06:33.122+00:00",
    "status": 401,
    "error": "Unauthorized",
    "message": "yoyo",
    "path": "/greeting"
}

I guess you don't see the exception you have thrown because the "greeting" endpoint is secured and spring security handle it itself.

Set "/greeting" not secured so you can access it and get the custom error

@Override
protected void configure(HttpSecurity http) throws Exception {
     http.csrf().disable().authorizeRequests().antMatchers("/**").permitAll();
}
Related