I have a Spring Boot API(localhost:8001
), which sends a request to another backend service with different domain(localhost:8080
)
@GetMapping("/test")
@PreAuthorize("isAuthenticated()")
public Mono<Object> getThingsboardTest() {
String token = “valid-token”
WebClient webClient = WebClient.create();
return webClient
.get()
.uri("http://localhost:8080/api/admin/featuresInfo")
.headers(h -> {
String authHeader = "Bearer " + token;
h.set("Authorization", authHeader);
log.info("Authorization Header: " + authHeader);
})
.retrieve()
.onStatus(HttpStatusCode::isError, clientResponse ->
clientResponse.bodyToMono(String.class)
.flatMap(errorBody -> {
log.error("Error Body: " + errorBody);
return Mono.error(new RuntimeException("Error Getting test: " + clientResponse.statusCode()));
})
)
.bodyToMono(Object.class);
}
However, when I use Postman call this endpoint, it returns 403 without response body.
What I tried
- Remove all WebClient code and return String
@GetMapping("/test")
@PreAuthorize("isAuthenticated()")
public String getThingsboardTest() {
String token = “valid-token”
return token
}
It returns successfully, so the problem has no relationship with my authentication logic
- Test another api
I tested another external which doesn’t require authentication, and it works perfectly.
private void loginTest() {
WebClient webClient = WebClient.create();
String jsonBody = "{"username":"sysadmin@test”,”password”:”test”}”;
Mono<LoginResponse> response = webClient.post()
.uri("http://localhost:8080/api/auth/login")
.contentType(MediaType.APPLICATION_JSON)
.bodyValue(jsonBody)
.retrieve()
.bodyToMono(ThingsboardLoginResponse.class);
response.subscribe(result -> {
redisTemplate.opsForValue().set(“token”, result.getToken());
});
}
So, the problem has no relationship with CORS
- Comment out the
.header
method
I commented out the method as follows
return webClient
.get()
.uri("http://localhost:8080/api/admin/featuresInfo")
// .headers(h -> {
// String authHeader = "Bearer " + token;
// h.set("Authorization", authHeader);
// log.info("Authorization Header: " + authHeader);
// })
.retrieve()
.onStatus(HttpStatusCode::isError, clientResponse ->
clientResponse.bodyToMono(String.class)
.flatMap(errorBody -> {
log.error("Error Body: " + errorBody);
return Mono.error(new RuntimeException("Error Getting Thingsboard test: " + clientResponse.statusCode()));
})
)
.bodyToMono(Object.class);
An interesting thing happens. I saw a log in my application
Error Body: {"status":401,"message":"Authentication failed","errorCode":10,"timestamp":1723832741717}
I didn’t see it when the header was uncommented.
Based on my trial above, I think the problem mostly relates to how I return the value in this method, but I still have no clue of the solution.