Проблема
У меня есть приложение Spring Boot с веб-сокетом (STOMP). Я отправляю JSON-строки через esp8260. Все нормально, если Spring Security выключен, но если я включаю Security, я получаю сообщение об ошибке:
2019-03-12 19:42:46.729 INFO 6568 --- [nio-8080-exec-2] o.apache.coyote.http11.Http11Processor : Error parsing HTTP request header
Note: further occurrences of HTTP request parsing errors will be logged at DEBUG level.
java.lang.IllegalArgumentException: Invalid character found in the HTTP protocol
at org.apache.coyote.http11.Http11InputBuffer.parseRequestLine(Http11InputBuffer.java:531) ~[tomcat-embed-core-9.0.13.jar:9.0.13]
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:294) ~[tomcat-embed-core-9.0.13.jar:9.0.13]
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66) [tomcat-embed-core-9.0.13.jar:9.0.13]
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:791) [tomcat-embed-core-9.0.13.jar:9.0.13]
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1417) [tomcat-embed-core-9.0.13.jar:9.0.13]
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) [tomcat-embed-core-9.0.13.jar:9.0.13]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [na:1.8.0_201]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [na:1.8.0_201]
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-9.0.13.jar:9.0.13]
at java.lang.Thread.run(Thread.java:748) [na:1.8.0_201]
Как исправить
Кто-нибудь знает, в чем проблема?
Я могу поставить больше кода, если хотите.
WebSecurityConfig
Если я закомментирую одну строку в методе настройки -. AnyRequest (). Authenticated (), все в порядке
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsService userDetailsService;
@Bean
public BCryptPasswordEncoder bCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/resources/**", "/registration","/gs-guide-websocket").permitAll()
//If I comment out the line below, everything is fine
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.logout()
.permitAll();
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder());
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
WebSocketConfig
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/commands", "/readings");
config.setApplicationDestinationPrefixes("/esp");
}
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/gs-guide-websocket").withSockJS();
}
}
AuthChannelInterceptorAdapter
Я получаю сообщения в методе preSend, только если Spring Security выключен.
@Slf4j
@Service
public class AuthChannelInterceptorAdapter implements ChannelInterceptor {
private static final String USERNAME_HEADER = "login";
private static final String PASSWORD_HEADER = "passcode";
@Autowired
private UserDetailsService userDetailsService;
@Autowired
private AuthenticationManager authenticationManager;
@Override
public Message<?> preSend(final Message<?> message, final MessageChannel channel) throws AuthenticationException {
final StompHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class);
log.info("Trying to authentication...");
log.info(accessor.toString());
if (StompCommand.CONNECT == accessor.getCommand()) {
final String username = accessor.getFirstNativeHeader(USERNAME_HEADER);
final String password = accessor.getFirstNativeHeader(PASSWORD_HEADER);
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
UsernamePasswordAuthenticationToken user = new UsernamePasswordAuthenticationToken(userDetails, password, userDetails.getAuthorities());
accessor.setUser(user);
log.info("StompCommand.CONNECT ");
}
return message;
}
/*...*/
}
Зависимости
buildscript {
ext {
springBootVersion = '2.1.1.RELEASE'
}
repositories {
mavenCentral()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
}
}
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
group = 'com.smartpi'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = 1.8
repositories {
mavenCentral()
}
dependencies {
implementation('org.springframework.boot:spring-boot-starter-data-jpa')
implementation('org.springframework.boot:spring-boot-starter-web')
implementation('org.springframework.security:spring-security-config')
implementation('org.springframework.security:spring-security-core')
implementation('org.springframework.security:spring-security-crypto')
implementation('org.springframework.security:spring-security-web')
compile group: 'org.springframework.boot', name: 'spring-boot-starter-websocket', version: '2.0.4.RELEASE'
compile group: 'org.springframework', name: 'spring-messaging', version: '4.0.0.RELEASE'
compile group: 'org.springframework.security', name: 'spring-security-messaging', version: '4.0.1.RELEASE'
compile group: 'org.springframework.security.oauth', name: 'spring-security-oauth2', version: '1.0.0.RELEASE'
compile group: 'org.springframework.boot', name: 'spring-boot-starter-security', version: '2.1.1.RELEASE'
compile group: 'javax.servlet', name: 'jstl'
compile group: 'org.apache.tomcat.embed', name: 'tomcat-embed-jasper'
runtimeOnly('com.h2database:h2')
compileOnly('org.projectlombok:lombok')
testImplementation('org.springframework.boot:spring-boot-starter-test')
}
Мой клиент - ESP8260 ESP-01, как я упоминал ранее.
Я использую эту библиотеку для esp: https://github.com/ukmaker/StompClient
Пример
Как я отправляю сообщения в приложение Spring Boot:
#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <WebSocketsClient.h>
#include "StompClient.h"
/**
* WiFi settings
**/
const char* wlan_ssid = "dlink";
const char* wlan_password = "";
/**
* Stomp server settings
**/
bool useWSS = false;
const char* ws_host = "192.168.0.101";
const int ws_port = 8080;
const char* ws_baseurl = "/gs-guide-websocket/"; // don't forget leading and trailing "/" !!!
bool sample = false;
// VARIABLES
WebSocketsClient webSocket;
Stomp::StompClient stomper(webSocket, ws_host, ws_port, ws_baseurl, true);
void setup() {
// setup serial
Serial.begin(115200);
// flush it - ESP Serial seems to start with rubbish
Serial.println();
// connect to WiFi
Serial.println("Logging into WLAN: " + String(wlan_ssid));
Serial.print(" ...");
WiFi.begin(wlan_ssid, wlan_password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println(" success.");
Serial.print("IP: "); Serial.println(WiFi.localIP());
// Start the StompClient
stomper.onConnect(subscribe);
stomper.onError(error);
if (useWSS) {
stomper.beginSSL();
} else {
stomper.begin();
}
}
// Once the Stomp connection has been made, subscribe to a topic
void subscribe(Stomp::StompCommand cmd) {
Serial.println("Connected to STOMP broker");
stomper.subscribe("/commands/blink", Stomp::CLIENT, handleBlinkMessage);
stomper.subscribe("/commands/sample", Stomp::CLIENT, handleSampleMessage);
}
void error(const Stomp::StompCommand cmd) {
Serial.println("ERROR: " + cmd.body);
}
Stomp::Stomp_Ack_t handleBlinkMessage(const Stomp::StompCommand cmd) {
Serial.println("Got a message!");
Serial.println(cmd.body);
return Stomp::CONTINUE;
}
Stomp::Stomp_Ack_t handleSampleMessage(const Stomp::StompCommand cmd) {
Serial.println("Got a message!");
Serial.println(cmd.body);
sample = true;
return Stomp::CONTINUE;
}
void takeSample() {
Serial.println("Take sample...");
stomper.sendMessage("/esp/sensors", "{\\\"id\\\":\\\"ESP8266_1\\\",\\\"label\\\":\\\"ESP8266 ESP-01\\\",\\\"internalIP\\\":null,\\\"modules\\\":[{\\\"type\\\":\\\"switch\\\",\\\"internalId\\\":1,\\\"label\\\":\\\"Pokój Marcina: światło\\\",\\\"isOn\\\":false},{\\\"type\\\":\\\"temperatureSensor\\\",\\\"internalId\\\":1,\\\"label\\\":\\\"Termometr: pokój\\\",\\\"temperature\\\":34.22}]}");
}
void loop() {
webSocket.loop();
takeSample();
}
UPDATE
Найдено что-то в хранилище apahe (531 строка):
https://ci.apache.org/projects/tomcat/tomcat9/coverage/org.apache.coyote.http11.Http11InputBuffer.html
Метод HttpParser.isHttpProtocol
(255 и 86):
https://ci.apache.org/projects/tomcat/tomcat9/coverage/org.apache.tomcat.util.http.parser.HttpParser.html
Возможно, клиент отправляет неверные данные, но я не знаю, что мне искать.
ОБНОВЛЕНИЕ .v2
Я включил дебаг на ESP
Отправлено сообщение от ESP:
[WS-Client] connect ws...
[WS-Client] connected to 192.168.0.100:80.
[WS-Client][sendHeader] sending header...
[WS-Client][sendHeader] handshake GET /socket/243/726111/websocket HTTP/1.1
Host: 192.168.0.100:80
Connection: Upgrade
Upgrade: websocket
Sec-WebSocket-Version: 13
Sec-WebSocket-Key: xQN1skwd8HNgHQg9taqSpQ==
Sec-WebSocket-Protocol: arduino
User-Agent: arduino-WebSocket-Client
Поступило:
[WS-Client][sendHeader] sending header... Done (21608us).
[WS-Client][handleHeader] RX: HTTP/1.1 302
[WS-Client][handleHeader] RX: Set-Cookie: JSESSIONID=43B94478F616F01BD3B8BDBB34ADDEDE; Path=/; HttpOnly
[WS-Client][handleHeader] RX: X-Content-Type-Options: nosniff
[WS-Client][handleHeader] RX: X-XSS-Protection: 1; mode=block
[WS-Client][handleHeader] RX: Cache-Control: no-cache, no-store, max-age=0, must-revalidate
[WS-Client][handleHeader] RX: Pragma: no-cache
[WS-Client][handleHeader] RX: Expires: 0
[WS-Client][handleHeader] RX: X-Frame-Options: SAMEORIGIN
[WS-Client][handleHeader] RX: Location: http://192.168.0.100/login
[WS-Client][handleHeader] RX: Content-Length: 0
[WS-Client][handleHeader] RX: Date: Sat, 16 Mar 2019 11:35:26 GMT
[WS-Client][handleHeader] Header read fin.
[WS-Client][handleHeader] Client settings:
[WS-Client][handleHeader] - cURL: /socket/243/726111/websocket
[WS-Client][handleHeader] - cKey: xQN1skwd8HNgHQg9taqSpQ==
[WS-Client][handleHeader] Server header:
[WS-Client][handleHeader] - cCode: 302
[WS-Client][handleHeader] - cIsUpgrade: 0
[WS-Client][handleHeader] - cIsWebsocket: 0
[WS-Client][handleHeader] - cAccept:
[WS-Client][handleHeader] - cProtocol: arduino
[WS-Client][handleHeader] - cExtensions:
[WS-Client][handleHeader] - cVersion: 0
[WS-Client][handleHeader] - cSessionId: 43B94478F616F01BD3B8BDBB34ADDEDE
[WS-Client][handleHeader] no Websocket connection close.
[WS-Client] client disconnected.