diff --git a/application/src/main/java/org/thingsboard/server/config/ApiKeyHandshakeInterceptor.java b/application/src/main/java/org/thingsboard/server/config/ApiKeyHandshakeInterceptor.java index d6d550b2c0..db839adc10 100644 --- a/application/src/main/java/org/thingsboard/server/config/ApiKeyHandshakeInterceptor.java +++ b/application/src/main/java/org/thingsboard/server/config/ApiKeyHandshakeInterceptor.java @@ -20,6 +20,7 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; import org.springframework.http.server.ServerHttpRequest; import org.springframework.http.server.ServerHttpResponse; +import org.springframework.security.core.AuthenticationException; import org.springframework.stereotype.Component; import org.springframework.web.socket.WebSocketHandler; import org.springframework.web.socket.server.HandshakeInterceptor; @@ -35,14 +36,13 @@ import java.util.Map; @RequiredArgsConstructor public class ApiKeyHandshakeInterceptor implements HandshakeInterceptor { - public static final String API_KEY_HEADER = "X-API-Key"; public static final String API_KEY_SECURITY_CTX_ATTR = "apiKeySecurityCtx"; private final ApiKeyAuthenticationProvider apiKeyAuthenticationProvider; @Override public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map attributes) { - String apiKey = request.getHeaders().getFirst(API_KEY_HEADER); + String apiKey = extractApiKey(request); if (apiKey != null) { if (apiKey.isEmpty()) { log.debug("Empty API key provided during WS handshake"); @@ -52,8 +52,8 @@ public class ApiKeyHandshakeInterceptor implements HandshakeInterceptor { try { SecurityUser securityUser = apiKeyAuthenticationProvider.authenticate(apiKey); attributes.put(API_KEY_SECURITY_CTX_ATTR, securityUser); - } catch (Exception e) { - log.debug("API key authentication failed during WS handshake: {}", e.getMessage()); + } catch (AuthenticationException e) { + log.warn("API key authentication failed during WS handshake: {}", e.getMessage()); response.setStatusCode(HttpStatus.UNAUTHORIZED); return false; } @@ -61,6 +61,17 @@ public class ApiKeyHandshakeInterceptor implements HandshakeInterceptor { return true; } + private String extractApiKey(ServerHttpRequest request) { + String header = request.getHeaders().getFirst(ThingsboardSecurityConfiguration.AUTHORIZATION_HEADER); + if (header == null) { + header = request.getHeaders().getFirst(ThingsboardSecurityConfiguration.AUTHORIZATION_HEADER_V2); + } + if (header != null && header.startsWith(ThingsboardSecurityConfiguration.API_KEY_HEADER_PREFIX)) { + return header.substring(ThingsboardSecurityConfiguration.API_KEY_HEADER_PREFIX.length()); + } + return null; + } + @Override public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception exception) { // no-op diff --git a/application/src/test/java/org/thingsboard/server/controller/AbstractControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/AbstractControllerTest.java index fa43f83ff2..7ddca44b36 100644 --- a/application/src/test/java/org/thingsboard/server/controller/AbstractControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/AbstractControllerTest.java @@ -122,7 +122,7 @@ public abstract class AbstractControllerTest extends AbstractNotifyEntityTest { } protected TbTestWebSocketClient buildAndConnectWebSocketClientWithApiKeyHeader(String apiKey) throws URISyntaxException, InterruptedException { - TbTestWebSocketClient wsClient = new TbTestWebSocketClient(new URI(WS_URL + wsPort + "/api/ws"), Map.of("X-API-Key", apiKey)); + TbTestWebSocketClient wsClient = new TbTestWebSocketClient(new URI(WS_URL + wsPort + "/api/ws"), Map.of("X-Authorization", "ApiKey " + apiKey)); assertThat(wsClient.connectBlocking(TIMEOUT, TimeUnit.SECONDS)).isTrue(); return wsClient; } diff --git a/application/src/test/java/org/thingsboard/server/controller/ApiKeyWebSocketApiTest.java b/application/src/test/java/org/thingsboard/server/controller/ApiKeyWebSocketApiTest.java index 375d37ae95..eead1d6a47 100644 --- a/application/src/test/java/org/thingsboard/server/controller/ApiKeyWebSocketApiTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/ApiKeyWebSocketApiTest.java @@ -86,7 +86,7 @@ public class ApiKeyWebSocketApiTest extends WebSocketApiTest { @Test public void testInvalidApiKeyHeader_connectionRejected() throws Exception { TbTestWebSocketClient client = new TbTestWebSocketClient( - new URI(WS_URL + wsPort + "/api/ws"), Map.of("X-API-Key", "invalid-key")); + new URI(WS_URL + wsPort + "/api/ws"), Map.of("X-Authorization", "ApiKey invalid-key")); try { boolean connected = client.connectBlocking(TIMEOUT, TimeUnit.SECONDS); assertThat(connected).isFalse(); @@ -98,7 +98,7 @@ public class ApiKeyWebSocketApiTest extends WebSocketApiTest { @Test public void testEmptyApiKeyHeader_connectionRejected() throws Exception { TbTestWebSocketClient client = new TbTestWebSocketClient( - new URI(WS_URL + wsPort + "/api/ws"), Map.of("X-API-Key", "")); + new URI(WS_URL + wsPort + "/api/ws"), Map.of("X-Authorization", "ApiKey ")); try { boolean connected = client.connectBlocking(TIMEOUT, TimeUnit.SECONDS); assertThat(connected).isFalse();