ResponseStatusException - response body missing - spring

I am using
id 'org.springframework.boot' version '2.6.1'
Following is the code snippet I am using for throwing the exception
catch(Exception e){
System.out.println(e.getLocalizedMessage());
throw new ResponseStatusException(
HttpStatus.BAD_REQUEST, "something went wrong",e);
}
In response, we are getting 400 without a response body
* upload completely sent off: 100 out of 100 bytes
* Mark bundle as not supporting multiuse
< HTTP/1.1 400
< Vary: Origin
< Vary: Access-Control-Request-Method
< Vary: Access-Control-Request-Headers
< X-Content-Type-Options: nosniff
< X-XSS-Protection: 1; mode=block
< Cache-Control: no-cache, no-store, max-age=0, must-revalidate
< Pragma: no-cache
< Expires: 0
< X-Frame-Options: DENY
< Content-Length: 0
< Date: Thu, 27 Oct 2022 14:43:38 GMT
< Connection: close
<
* Closing connection 0
UPDATE: application.properties has the following config
server.error.include-message=always
What am I missing?

Starting from 2.3, Spring Boot doesn't include an error message on the default error page. The reason is to reduce the risk of leaking information to a client.
To change the default behavior, you can set the server.error.include-message property:
server.error.include-message=always
Source:
https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.3-Release-Notes#changes-to-the-default-error-pages-content

Related

Spring Boot Controller sends "Connection: close" on big body

I have a Spring Boot Application (2.7.3) with a POST-Endpoint that is secured via Basic Auth. The client has reported problems connecting to the endpoint because he does use non-preemptive-authentication and receives a "Connection: close" on the first request without credentials. After receiving the Header Field, the client does not send a second request including the credentials and therefore cannot connect to the Application.
After investigation I found out that the "Connection: close" header is only send on a request with a rather large body. Is this expected behaviour?
Curl Requests:
#>curl -H "Accept: application/json" -v -d "#<path-to-file>\big_body.json" http://127.0.0.1:8080/demo/updateSapStatus
* Trying 127.0.0.1:8080...
* Connected to 127.0.0.1 (127.0.0.1) port 8080 (#0)
> POST /demo/updateSapStatus HTTP/1.1
> Host: 127.0.0.1:8080
> User-Agent: curl/7.83.1
> Accept: application/json
> Content-Length: 1585745
> Content-Type: application/x-www-form-urlencoded
> Expect: 100-continue
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 100
* Mark bundle as not supporting multiuse
< HTTP/1.1 401
< WWW-Authenticate: Basic realm="Realm"
< X-Content-Type-Options: nosniff
< X-XSS-Protection: 1; mode=block
< Cache-Control: no-cache, no-store, max-age=0, must-revalidate
< Pragma: no-cache
< Expires: 0
< X-Frame-Options: SAMEORIGIN
< Content-Length: 0
< Date: Thu, 17 Nov 2022 08:01:18 GMT
< Connection: close
<
* we are done reading and this is set to close, stop send
* Closing connection 0
#>curl -H "Accept: application/json" -v -d "[]" http://127.0.0.1:8080/demo/updateSapStatus
* Trying 127.0.0.1:8080...
* Connected to 127.0.0.1 (127.0.0.1) port 8080 (#0)
> POST /demo/updateSapStatus HTTP/1.1
> Host: 127.0.0.1:8080
> User-Agent: curl/7.83.1
> Accept: application/json
> Content-Length: 2
> Content-Type: application/x-www-form-urlencoded
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 401
< WWW-Authenticate: Basic realm="Realm"
< X-Content-Type-Options: nosniff
< X-XSS-Protection: 1; mode=block
< Cache-Control: no-cache, no-store, max-age=0, must-revalidate
< Pragma: no-cache
< Expires: 0
< X-Frame-Options: SAMEORIGIN
< Content-Length: 0
< Date: Thu, 17 Nov 2022 08:01:22 GMT
<
* Connection #0 to host 127.0.0.1 left intact
Controller:
#RestController
#RequestMapping("/demo")
public class DemoController {
#PostMapping(value = "/updateSapStatus")
public ResponseEntity<String> updateSapStatus(#RequestBody List<ChangeSapStatusRequestBody> requestBody) {
return ResponseEntity.of(Optional.of("Hello"));
}
}
Basic Auth Configuration:
#EnableWebSecurity
public class BasicAuthSecurityConfiguration {
private static final String USER = "USER";
#Bean
public AuthenticationProvider basicAuthenticationProvider() {
PasswordEncoder encoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
UserDetails user = User.builder()
.username("username")
.password(encoder.encode("password"))
.roles(USER)
.build();
InMemoryUserDetailsManager userManager = new InMemoryUserDetailsManager(user);
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
provider.setUserDetailsService(userManager);
return provider;
}
#Bean
#Order(1)
public SecurityFilterChain basicAuthFilterChain(HttpSecurity http) throws Exception {
return http
.requestMatchers(requestMatchers -> requestMatchers.anyRequest())
.authorizeRequests(authorizeRequests -> authorizeRequests.anyRequest().hasRole(USER))
.csrf(CsrfConfigurer::disable)
.headers(headers -> headers.frameOptions(FrameOptionsConfig::sameOrigin))
.httpBasic(withDefaults())
.build();
}
}

What is stopping browser to download pdf file

I am using spring boot application to download a jasper file, which is downloaded in my system, on browser. The code that I have written is:
#GetMapping("/download")
public ResponseEntity downloadFileFromLocal(HttpServletResponse response) throws JRException, SQLException, IOException {
Path path = Paths.get(System.getProperty("user.home") + "//downloads//" + "fileName.pdf");
Resource resource = null;
try {
resource = new UrlResource(path.toUri());
} catch (MalformedURLException e) {
e.printStackTrace();
}
return ResponseEntity.ok()
.contentType(MediaType.parseMediaType("application/download"))
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + resource.getFilename() + "\"")
.body(resource);
}
This code is working perfectly fine and my file is being downloaded on browser when I try this code in a simple application i.e. practice approach. However, when I try to download the file in my main application with the same code then nothing happens. I am not sure what is stopping browser to download my pdf file in browser. The expected result is:
I think there is some problem with the response Headers.
Following is the response header for my Practice application file:
Accept-Ranges: bytes
Connection: keep-alive
Content-Disposition: attachment; filename="Daily%20Report%20For%20Site_ID%201.pdf"
Content-Length: 2869
Content-Type: application/download
Date: Mon, 27 Dec 2021 10:48:24 GMT
Keep-Alive: timeout=60
And for main application, response headers are:
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Content-Length: 0
Date: Mon, 27 Dec 2021 11:28:33 GMT
Expires: 0
Pragma: no-cache
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
X-XSS-Protection: 1; mode=block

Why Spring Boot application does not work with relative quality factor of HTTP?

Lets suppose I have a Spring Boot application:
dependencies {
implementation("org.springframework.boot:spring-boot-starter-webflux:2.4.0")
}
with a simple RestController:
#RestController
class TestController {
#PostMapping("/test")
suspend fun test(#RequestBody request: Map<String, String>) {
throw RuntimeException("test")
}
}
When I use httpie client to make requests, then the result looks like:
➜ ~ http post :8080/test param=value --verbose
POST /test HTTP/1.1
Accept: application/json, */*;q=0.5
Accept-Encoding: gzip, deflate
Connection: keep-alive
Content-Length: 18
Content-Type: application/json
Host: localhost:8080
User-Agent: HTTPie/2.3.0
{
"param": "value"
}
HTTP/1.1 500 Internal Server Error
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Content-Length: 309
Content-Type: text/html;charset=UTF-8
Expires: 0
Pragma: no-cache
Referrer-Policy: no-referrer
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
X-XSS-Protection: 1 ; mode=block
<html><body><h1>Whitelabel Error Page</h1><p>This application has no configured error view, so you are seeing this as a fallback.</p><div id='created'>Wed Dec 09 22:24:52 MSK 2020</div><div>[906bb33e-5] There was an unexpected error (type=Internal Server Error, status=500).</div><div>test</div></body></html>
When I use cURL:
➜ ~ curl -XPOST localhost:8080/test -d '{"param": "value"}' -H "Content-type: application/json" -v
Note: Unnecessary use of -X or --request, POST is already inferred.
* Trying ::1...
* TCP_NODELAY set
* Connected to localhost (::1) port 8080 (#0)
> POST /test HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.64.1
> Accept: */*
> Content-type: application/json
> Content-Length: 18
>
* upload completely sent off: 18 out of 18 bytes
< HTTP/1.1 500 Internal Server Error
< Content-Type: application/json
< Content-Length: 170
< Cache-Control: no-cache, no-store, max-age=0, must-revalidate
< Pragma: no-cache
< Expires: 0
< X-Content-Type-Options: nosniff
< X-Frame-Options: DENY
< X-XSS-Protection: 1 ; mode=block
< Referrer-Policy: no-referrer
<
* Connection #0 to host localhost left intact
{"timestamp":1607541989698,"path":"/test","status":500,"error":"Internal Server Error","message":"test","requestId":"7f249f68-9","exception":"java.lang.RuntimeException"}* Closing connection 0
The difference in header Accept. Httpie uses Accept: application/json, */*;q=0.5 header with relative quality factor 0.5 and, despite the fact that I requested JSON if possible (and it's possible), the application returns HTML representation.
Is it how Spring Boot should work and I do something wrong?
UPD: Everything works fine with Tomcat (spring-boot-starter-web). After some debugging I found that, when Tomcat used, errors are handled by org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController and for Netty it is org.springframework.boot.autoconfigure.web.reactive.error.DefaultErrorWebExceptionHandler. It looks like the source of misbehavior is here: when MediaType.ALL is removed from acceptedMediaTypes .

What might be wrong with Spring Security Config? Get vs POST

I have a Rest Controller and two endpoints there. While I can do basic authentication to GET end endpoint, I can not perform the same for the POST endpoint.
Please see the curl executions for GET and POST below, for some reason POST endpoint returns 401.
What might be the reason? Any ideas? As you can check in Security Config at bottom, I do not have special cases for GET/POST.
GET TRY:
curl -v -u user:password -X GET http://localhost:8080/api/clusters/
Note: Unnecessary use of -X or --request, GET is already inferred.
* Trying ::1...
* TCP_NODELAY set
* Connected to localhost (::1) port 8080 (#0)
* Server auth using Basic with user 'user'
> GET /api/clusters/ HTTP/1.1
> Host: localhost:8080
> Authorization: Basic dXNlcjpwYXNzd29yZA==
> User-Agent: curl/7.64.1
> Accept: */*
>
< HTTP/1.1 200
< X-Content-Type-Options: nosniff
< X-XSS-Protection: 1; mode=block
< Cache-Control: no-cache, no-store, max-age=0, must-revalidate
< Pragma: no-cache
< Expires: 0
< X-Frame-Options: DENY
< Content-Type: application/json
< Transfer-Encoding: chunked
< Date: Fri, 20 Dec 2019 12:50:44 GMT
<
* Connection #0 to host localhost left intact
[{"id":1,"name":"Alfa","securityProtocol":"PLAINTEXT","sslTruststorePassword":null,"sslKeystorePassword":null,"sslKeyPassword":null,"trustStore":null,"keyStore":null,"saslMechanism":"","saslJaasConfig":"","clientId":"Ali","requestTimeoutMs":30000,"version":0,"properties":[],"bootstrapServers":"kafka1:9093"}]* Closing connection 0
POST TRY:
kaan$ curl -v -u user:password -X POST http://localhost:8080/api/clusters/ -d {"name":"Beta"}
Note: Unnecessary use of -X or --request, POST is already inferred.
* Trying ::1...
* TCP_NODELAY set
* Connected to localhost (::1) port 8080 (#0)
* Server auth using Basic with user 'user'
> POST /api/clusters/ HTTP/1.1
> Host: localhost:8080
> Authorization: Basic dXNlcjpwYXNzd29yZA==
> User-Agent: curl/7.64.1
> Accept: */*
> Content-Length: 11
> Content-Type: application/x-www-form-urlencoded
>
* upload completely sent off: 11 out of 11 bytes
< HTTP/1.1 401
< Set-Cookie: JSESSIONID=022342C09075AC29AC703547FD5A0E45; Path=/; HttpOnly
< X-Content-Type-Options: nosniff
< X-XSS-Protection: 1; mode=block
< Cache-Control: no-cache, no-store, max-age=0, must-revalidate
< Pragma: no-cache
< Expires: 0
< X-Frame-Options: DENY
* Authentication problem. Ignoring this.
< WWW-Authenticate: Basic realm="Realm"
< Content-Length: 0
< Date: Fri, 20 Dec 2019 12:51:38 GMT
<
* Connection #0 to host localhost left intact
* Closing connection 0
And my Security Config is as:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.NEVER).and()
.authorizeRequests()
.antMatchers("/css/**", "/js/**", "/img/**", "/vendor/**", "/login").permitAll()
.anyRequest().authenticated()
.and()
.httpBasic()
.and()
.formLogin()
.loginPage("/login.html")
.permitAll()
.and()
.logout()
.permitAll();
}
This solved the issue:
.csrf().disable()

"Missing grant type" despite "Content-Type: application/x-www-form-urlencoded"

Similar to this question I am sending the following POST to the server:
content-type: application/x-www-form-urlencoded
authorization: Basic dGVzdGp3dGNsaWVudGlkOlhZN2ttem9OemwxMDA=
accept: */*
With the payload:
{"username"="test", "password": "pw", "email": "test#example.com"}
However, I'm still getting
{
"error": "invalid_request",
"error_description": "Missing grant type"
}
as response from the server. Any idea why this is not working?
Note that the request is working if I use curl:
$ curl testjwtclientid:XY7kmzoNzl100#localhost:8080/oauth/token -d grant_type=password -d username=john.doe -d password=pw -v
* Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 8080 (#0)
* Server auth using Basic with user 'testjwtclientid'
> POST /oauth/token HTTP/1.1
> Host: localhost:8080
> Authorization: Basic dGVzdGp3dGNsaWVudGlkOlhZN2ttem9OemwxMDA=
> User-Agent: curl/7.47.0
> Accept: */*
> Content-Length: 49
> Content-Type: application/x-www-form-urlencoded
>
* upload completely sent off: 49 out of 49 bytes
< HTTP/1.1 200
< X-Content-Type-Options: nosniff
< X-XSS-Protection: 1; mode=block
< Cache-Control: no-cache, no-store, max-age=0, must-revalidate
< Pragma: no-cache
< Expires: 0
< X-Frame-Options: DENY
< Cache-Control: no-store
< Pragma: no-cache
< Content-Type: application/json;charset=UTF-8
< Transfer-Encoding: chunked
< Date: Thu, 02 Nov 2017 19:21:29 GMT
<
* Connection #0 to host localhost left intact
{"access_token":"<an-ugly-long-token>","token_type":"bearer","expires_in":43199,"scope":"read write","jti":"80e2b6af-d999-4fb6-a4cd-5e6ab9c3fcaa"}
Your payload should be something like grant_type=password&username=john.doe&password=pw, whereas you are passing it as JSON. You can check Spring security OAuth2 accept JSON for JSON payload

Resources