lealceldeiro
lealceldeiro

Reputation: 14998

401 instead of 403 with Spring Boot 2

With Spring Boot 1.5.6.RELEASE I was able to send HTTP Status code 401 instead of 403 as described in How let spring security response unauthorized(http 401 code) if requesting uri without authentication, by doing this:

public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //...
        http.exceptionHandling()
                .authenticationEntryPoint(new Http401AuthenticationEntryPoint("myHeader"));
        //...
    }
}

using the org.springframework.boot.autoconfigure.security.Http401AuthenticationEntryPoint class.

I just upgraded to Spring Boot 2.0.0.RELEASE and found there is not such class any more (at least in that package).

Questions:


Please notice this is different from Spring Security anonymous 401 instead of 403 because it's referring specifically to SpringBoot 2 (there are solutions in that post not applicable anymore in SpringBoot version 2 or others are not needed at all)

Upvotes: 18

Views: 17333

Answers (5)

Roman T
Roman T

Reputation: 2000

For reactive (WebFlux) stack you can override the returned status code by adding such @Bean to catch some specific exceptions:

@Component
class MyErrorAttributes : DefaultErrorAttributes() {
override fun getErrorAttributes(
    request: ServerRequest,
    options: ErrorAttributeOptions
): MutableMap<String, Any> {
    val cause = super.getError(request)

    val errorAttributes = super.getErrorAttributes(request, options)

    when (cause) {
        is TokenExpiredException -> {
            errorAttributes["status"] = HttpStatus.UNAUTHORIZED.value()
            errorAttributes["error"] = HttpStatus.UNAUTHORIZED.reasonPhrase
        }
    }

    return errorAttributes
}
}

Upvotes: 1

lealceldeiro
lealceldeiro

Reputation: 14998

Heads up

By default Spring Boot 2 will return 401 when spring-boot-starter-security is added as a dependency and an unauthorized request is performed.

This may change if you place some custom configurations to modify the security mechanism behavior. If that's the case and you truly need to force the 401 status, then read the below original post.

Original Post

The class org.springframework.boot.autoconfigure.security.Http401AuthenticationEntryPoint was removed in favor of org.springframework.security.web.authentication.HttpStatusEntryPoint.

In my case the code would go like this:

public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //...
        http.exceptionHandling()
            .authenticationEntryPoint(new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED));
        //...
    }
}

Bonus

If you need to return some information in the response body or customize the response somehow you can do something like this:

1- Extend AuthenticationEntryPoint

public class MyEntryPoint implements AuthenticationEntryPoint {
    private final HttpStatus httpStatus;
    private final Object responseBody;

    public MyEntryPoint(HttpStatus httpStatus, Object responseBody) {
        Assert.notNull(httpStatus, "httpStatus cannot be null");
        Assert.notNull(responseBody, "responseBody cannot be null");
        this.httpStatus = httpStatus;
        this.responseBody = responseBody;
    }

    @Override
    public final void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException {
        response.setStatus(httpStatus.value());

        try (PrintWriter writer = response.getWriter()) {
            writer.print(new ObjectMapper().writeValueAsString(responseBody));
        }
    }
}

2- Provide an instance of MyEntryPoint to the security configuration

public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // customize your response body as needed
        Map<String, String> responseBody = new HashMap<>();
        responseBody.put("error", "unauthorized");

        //...
        http.exceptionHandling()
            .authenticationEntryPoint(new MyEntryPoint(HttpStatus.UNAUTHORIZED, responseBody));
        //...
    }
}

Upvotes: 35

dur
dur

Reputation: 17009

Http401AuthenticationEntryPoint was removed.

See Spring Boot Github Repo > Issue #10715 (Remove Http401AuthenticationEntryPoint):

Remove Http401AuthenticationEntryPoint

rwinch commented on 20 Oct 2017
As far as I can tell it is not being used in the Spring Boot code base, so it might be good to remove Http401AuthenticationEntryPoint.

Depending on your requirements, you could use:

Upvotes: 2

Tai Truong
Tai Truong

Reputation: 778

Just to elaborate @lealceldeiro's answer:

Before Spring Boot 2 my Securiy Configuration class looked like this:

@Configuration
public class MyConfig extends WebSecurityConfigurerAdapter {

    @Bean
    public Http401AuthenticationEntryPoint securityException401EntryPoint() {
      return new Http401AuthenticationEntryPoint("Bearer realm=\"webrealm\"");
    }

    @Autowired
    private Http401AuthenticationEntryPoint authEntrypoint;

    @Override
    protected void configure(HttpSecurity http) throws Exception {

      // some http configuration ...

      // Spring Boot 1.5.x style
      http.exceptionHandling().authenticationEntryPoint(authEntrypoint);
    }
//...
}

And now in Spring Boot 2 it looks like this:

@Configuration
public class MyConfig extends WebSecurityConfigurerAdapter {

    //Bean configuration for Http401AuthenticationEntryPoint can be removed

    //Autowiring also removed

    @Override
    protected void configure(HttpSecurity http) throws Exception {

      // some http configuration ...

      // Spring Boot 2 style
      http.exceptionHandling().authenticationEntryPoint(new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED));
    }
//...
}

See also this comment in Spring Boot Github Repo > PR Remove Http401AuthenticationEntryPoint.

Upvotes: 3

user2530251
user2530251

Reputation: 7

You can customize your logic with overriding the class AuthenticationEntryPoint this should be working:

@Component public class AuthEntryPointException implements AuthenticationEntryPoint, Serializable {

    private static final long serialVersionUID = -8970718410437077606L;

    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response,
        AuthenticationException authException) throws IOException {
        response.setStatus(HttpStatus.SC_UNAUTHORIZED);
        response.setContentType("application/json");
        response.getWriter().write("{\"result\":\"UNAUTHORIZED\",\"message\":\"UNAUTHORIZED or Invalid Token\"}");
    }
}

Upvotes: 0

Related Questions