- Notifications
You must be signed in to change notification settings - Fork 41.7k
Closed
Description
There is an inconsistency in the default 401 Unauthorized response.
If client credentials are not provided:
- 401 Unauthorized response is returned
- The response includes JSON with the error (like with any exception exposed by /error endpoint)
If wrong client credentials are provided:
- 401 Unauthorized response is returned
- The response does not include JSON
Sample pom file:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.4.5</version> <relativePath /> <!-- lookup parent from repository --> </parent> <groupId>org.eu.rubensa.springboot.error</groupId> <artifactId>springboot-error-test</artifactId> <version>0.0.1-SNAPSHOT</version> <name>springboot-error-test</name> <description>Project for testing Spring Boot error handling</description> <properties> <java.version>8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-test</artifactId> <scope>test</scope> </dependency> </dependencies> </project>Sample test class:
package org.eu.rubensa.springboot.error; import com.fasterxml.jackson.databind.JsonNode; import org.assertj.core.api.Assertions; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; import org.springframework.boot.test.web.client.TestRestTemplate; import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; /** * The @SpringBootTest annotation will load the fully ApplicationContext. This * will not use slicing and scan for all the stereotype annotations * (@Component, @Service, @Respository and @Controller / @RestController) and * loads the full application context. */ @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, // From Spring 2.3.0 "server.error.include-message" and // "server.error.include-binding-errors" is set to "never" properties = { "server.error.include-message=always", /** * When you add the Security starter without custom security configurations, * Spring Boot endpoints will be secured using HTTP basic authentication with a * default user and generated password. To override that, you can configure * credentials in application.properties as follows */ "spring.security.user.name=username", "spring.security.user.password=password" }) public class BadCredentialsInconsistencyTest { @Autowired private TestRestTemplate testRestTemplate; @Test public void testBadCredentials() throws Exception { final ResponseEntity<JsonNode> response = testRestTemplate.withBasicAuth("username", "wrongpassword") .exchange("/test", HttpMethod.GET, null, JsonNode.class); Assertions.assertThat(response.getStatusCode()).isEqualTo(HttpStatus.UNAUTHORIZED); JsonNode jsonResponse = response.getBody(); /** * Since Spring Boot 2.0 the /error end-point is protected so the exception is * not "exposed" as JSON. * <p> * see: https://github.com/spring-projects/spring-security/issues/4467 */ Assertions.assertThat(jsonResponse).isNull(); } @Test public void testNoCredentials() throws Exception { final ResponseEntity<JsonNode> response = testRestTemplate.exchange("/test", HttpMethod.GET, null, JsonNode.class); Assertions.assertThat(response.getStatusCode()).isEqualTo(HttpStatus.UNAUTHORIZED); JsonNode jsonResponse = response.getBody(); /** * Since Spring Boot 2.0 the /error end-point is protected so the exception is * not "exposed" as JSON. * <p> * Curiously if no credentials provided the error page is reached to "expose" * the exception as JSON. * <p> * see: https://github.com/spring-projects/spring-security/issues/4467 */ Assertions.assertThat(jsonResponse.findValue("status").asInt()).isEqualTo(401); Assertions.assertThat(jsonResponse.findValue("error").asText()).isEqualTo("Unauthorized"); Assertions.assertThat(jsonResponse.findValue("message").asText()).contains("Unauthorized"); Assertions.assertThat(jsonResponse.findValue("path").asText()).isEqualTo("/test"); } /** * A nested @Configuration class wild be used instead of the application’s * primary configuration. * <p> * Unlike a nested @Configuration class, which would be used instead of your * application’s primary configuration, a nested @TestConfiguration class is * used in addition to your application’s primary configuration. */ @Configuration /** * Tells Spring Boot to start adding beans based on classpath settings, other * beans, and various property settings. */ @EnableAutoConfiguration /** * The @ComponentScan tells Spring to look for other components, configurations, * and services in the the TestWebConfig package, letting it find the * TestController class. * <p> * We only want to test the classes defined inside this test configuration */ static class TestConfig { @RestController public static class TestController { @GetMapping("/test") public void doNothing() { } } } }Spring Boot Version: 2.4.5
Related issues:
Metadata
Metadata
Assignees
Labels
type: bugA general bugA general bug