diff --git a/backend/pom.xml b/backend/pom.xml
index 513a5dd9..0db16a65 100644
--- a/backend/pom.xml
+++ b/backend/pom.xml
@@ -216,6 +216,14 @@
org.postgresql
postgresql
+
+ org.springframework
+ spring-websocket
+
+
+ org.springframework
+ spring-messaging
+
javax.validation
validation-api
diff --git a/backend/src/main/java/AppConfig.java b/backend/src/main/java/AppConfig.java
index 2e4a06a4..23dba5ef 100644
--- a/backend/src/main/java/AppConfig.java
+++ b/backend/src/main/java/AppConfig.java
@@ -1,6 +1,8 @@
import java.time.Duration;
import javax.sql.DataSource;
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.ObjectMapper;
import liquibase.integration.spring.SpringLiquibase;
import net.javacrumbs.shedlock.core.LockProvider;
import net.javacrumbs.shedlock.provider.jdbctemplate.JdbcTemplateLockProvider;
@@ -81,4 +83,10 @@ public class AppConfig {
public RestTemplate restTemplate() {
return new RestTemplate();
}
+
+ @Bean
+ public ObjectMapper objectMapper() {
+ return new ObjectMapper()
+ .configure(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE, true);
+ }
}
diff --git a/backend/src/main/java/ru/micord/ervu/account_applications/security/provider/ErvuJwtAuthenticationProvider.java b/backend/src/main/java/ru/micord/ervu/account_applications/security/provider/ErvuJwtAuthenticationProvider.java
index 198cd59b..b57b6a7a 100644
--- a/backend/src/main/java/ru/micord/ervu/account_applications/security/provider/ErvuJwtAuthenticationProvider.java
+++ b/backend/src/main/java/ru/micord/ervu/account_applications/security/provider/ErvuJwtAuthenticationProvider.java
@@ -8,14 +8,18 @@ import ru.micord.ervu.account_applications.security.model.jwt.UserSession;
import ru.micord.ervu.account_applications.security.model.jwt.authentication.JwtTokenAuthentication;
import ru.micord.ervu.account_applications.security.model.jwt.authentication.JwtTokenDummy;
import ru.micord.ervu.account_applications.security.service.JwtTokenService;
+import ru.micord.ervu.account_applications.websocket.service.WebSocketService;
@Component
public class ErvuJwtAuthenticationProvider implements AuthenticationProvider {
private final JwtTokenService jwtTokenService;
+ private final WebSocketService webSocketService;
- public ErvuJwtAuthenticationProvider(JwtTokenService jwtTokenService) {
+ public ErvuJwtAuthenticationProvider(JwtTokenService jwtTokenService,
+ WebSocketService webSocketService) {
this.jwtTokenService = jwtTokenService;
+ this.webSocketService = webSocketService;
}
@Override
@@ -23,6 +27,7 @@ public class ErvuJwtAuthenticationProvider implements AuthenticationProvider {
JwtTokenDummy jwtTokenDummy = (JwtTokenDummy) authentication;
String jwtToken = jwtTokenDummy.getToken();
UserSession userSession = jwtTokenService.getUserSession(jwtToken);
+ webSocketService.connectToSocket();
return new JwtTokenAuthentication(userSession, jwtToken);
}
diff --git a/backend/src/main/java/ru/micord/ervu/account_applications/websocket/WebSocketConfig.java b/backend/src/main/java/ru/micord/ervu/account_applications/websocket/WebSocketConfig.java
new file mode 100644
index 00000000..853a6ab8
--- /dev/null
+++ b/backend/src/main/java/ru/micord/ervu/account_applications/websocket/WebSocketConfig.java
@@ -0,0 +1,18 @@
+package ru.micord.ervu.account_applications.websocket;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.socket.client.WebSocketClient;
+import org.springframework.web.socket.client.standard.StandardWebSocketClient;
+
+/**
+ * @author gulnaz
+ */
+@Configuration
+public class WebSocketConfig {
+
+ @Bean
+ public WebSocketClient webSocketStompClient() {
+ return new StandardWebSocketClient();
+ }
+}
diff --git a/backend/src/main/java/ru/micord/ervu/account_applications/websocket/dto/ProcessErrorMsg.java b/backend/src/main/java/ru/micord/ervu/account_applications/websocket/dto/ProcessErrorMsg.java
new file mode 100644
index 00000000..f6736468
--- /dev/null
+++ b/backend/src/main/java/ru/micord/ervu/account_applications/websocket/dto/ProcessErrorMsg.java
@@ -0,0 +1,10 @@
+package ru.micord.ervu.account_applications.websocket.dto;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+
+/**
+ * @author gulnaz
+ */
+@JsonIgnoreProperties(ignoreUnknown = true)
+public record ProcessErrorMsg(String message) {
+}
diff --git a/backend/src/main/java/ru/micord/ervu/account_applications/websocket/dto/ProcessResponseBody.java b/backend/src/main/java/ru/micord/ervu/account_applications/websocket/dto/ProcessResponseBody.java
new file mode 100644
index 00000000..4523e130
--- /dev/null
+++ b/backend/src/main/java/ru/micord/ervu/account_applications/websocket/dto/ProcessResponseBody.java
@@ -0,0 +1,12 @@
+package ru.micord.ervu.account_applications.websocket.dto;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+/**
+ * @author gulnaz
+ */
+@JsonIgnoreProperties(ignoreUnknown = true)
+public record ProcessResponseBody(String type, String userName, @JsonProperty("value") String tempPass,
+ String secretLink, ProcessErrorMsg msg) {
+}
diff --git a/backend/src/main/java/ru/micord/ervu/account_applications/websocket/dto/ProcessResponseDto.java b/backend/src/main/java/ru/micord/ervu/account_applications/websocket/dto/ProcessResponseDto.java
new file mode 100644
index 00000000..321fa20f
--- /dev/null
+++ b/backend/src/main/java/ru/micord/ervu/account_applications/websocket/dto/ProcessResponseDto.java
@@ -0,0 +1,12 @@
+package ru.micord.ervu.account_applications.websocket.dto;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import ru.micord.ervu.account_applications.websocket.enums.ClassName;
+
+/**
+ * @author gulnaz
+ */
+@JsonIgnoreProperties(ignoreUnknown = true)
+public record ProcessResponseDto(String traceId, String forUser, ClassName className,
+ ProcessResponseBody body) {
+}
diff --git a/backend/src/main/java/ru/micord/ervu/account_applications/websocket/enums/ClassName.java b/backend/src/main/java/ru/micord/ervu/account_applications/websocket/enums/ClassName.java
new file mode 100644
index 00000000..3d0f3cb3
--- /dev/null
+++ b/backend/src/main/java/ru/micord/ervu/account_applications/websocket/enums/ClassName.java
@@ -0,0 +1,31 @@
+package ru.micord.ervu.account_applications.websocket.enums;
+
+import java.util.Arrays;
+
+import com.fasterxml.jackson.annotation.JsonEnumDefaultValue;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+/**
+ * @author gulnaz
+ */
+public enum ClassName {
+ @JsonProperty("update")
+ UPDATE,
+ @JsonProperty("processError")
+ PROCESS_ERROR,
+ @JsonEnumDefaultValue
+ SKIP
+
+// private String value;
+//
+// ClassName(String value) {
+// this.value = value;
+// }
+//
+// public static ClassName getByValue(String value) {
+// return Arrays.stream(ClassName.values())
+// .filter(e -> e.value.equals(value))
+// .findFirst()
+// .orElse(ClassName.SKIP);
+// }
+}
diff --git a/backend/src/main/java/ru/micord/ervu/account_applications/websocket/handler/ClientSocketHandler.java b/backend/src/main/java/ru/micord/ervu/account_applications/websocket/handler/ClientSocketHandler.java
new file mode 100644
index 00000000..6421af16
--- /dev/null
+++ b/backend/src/main/java/ru/micord/ervu/account_applications/websocket/handler/ClientSocketHandler.java
@@ -0,0 +1,110 @@
+package ru.micord.ervu.account_applications.websocket.handler;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.context.annotation.Lazy;
+import org.springframework.stereotype.Component;
+import org.springframework.util.StringUtils;
+import org.springframework.web.socket.CloseStatus;
+import org.springframework.web.socket.TextMessage;
+import org.springframework.web.socket.WebSocketMessage;
+import org.springframework.web.socket.WebSocketSession;
+import org.springframework.web.socket.handler.TextWebSocketHandler;
+import ru.micord.ervu.account_applications.service.UserApplicationListService;
+import ru.micord.ervu.account_applications.websocket.dto.ProcessResponseDto;
+import ru.micord.ervu.account_applications.websocket.service.WebSocketService;
+
+/**
+ * @author gulnaz
+ */
+@Component
+public class ClientSocketHandler extends TextWebSocketHandler {
+ private static final Logger LOGGER = LoggerFactory.getLogger(TextWebSocketHandler.class);
+
+ private final List sessions = new CopyOnWriteArrayList<>();
+
+ private final ObjectMapper objectMapper;
+ private final UserApplicationListService applicationService;
+ private final WebSocketService webSocketService;
+
+ public ClientSocketHandler(ObjectMapper objectMapper,
+ UserApplicationListService applicationService,
+ @Lazy WebSocketService webSocketService) {
+ this.objectMapper = objectMapper;
+ this.applicationService = applicationService;
+ this.webSocketService = webSocketService;
+ }
+
+ @Override
+ public void afterConnectionEstablished(WebSocketSession session) {
+ LOGGER.info("established connection {}", session);
+ sessions.add(session);
+ }
+
+ @Override
+ public void handleMessage(WebSocketSession session, WebSocketMessage> message)
+ throws JsonProcessingException {
+ ProcessResponseDto dto = objectMapper.readValue(message.getPayload().toString(),
+ ProcessResponseDto.class);
+ LOGGER.info("received message, type = {}, sessionId = {}, principal = {}",
+ dto.className(), session.getId(), session.getPrincipal());
+ String traceId = dto.traceId();
+
+ switch (dto.className()) {
+ case UPDATE -> {
+ LOGGER.info("update by traceId = {}", traceId);
+ String tempPass = dto.body().tempPass();
+
+ if (StringUtils.hasText(tempPass)) {
+ applicationService.savePassword(traceId, tempPass);
+ }
+ else {
+ applicationService.saveAcceptedStatus(traceId);
+ }
+ }
+ case PROCESS_ERROR -> {
+ LOGGER.error("error by traceId = {}", traceId);
+ applicationService.saveError(traceId, dto.body().msg().message());
+ }
+ }
+ }
+
+ @Override
+ protected void handleTextMessage(WebSocketSession session, TextMessage message) {
+ LOGGER.info("received text message {}", message.getPayload());
+ }
+
+ @Override
+ public void handleTransportError(WebSocketSession session, Throwable exception) {
+ LOGGER.error("Transport error {}", exception.getMessage());
+
+ try {
+ session.close();
+ }
+ catch (IOException e) {
+ LOGGER.error("Failed to close session on handleTransportError ", e);
+ }
+ }
+
+ @Override
+ public void afterConnectionClosed(WebSocketSession session, CloseStatus status) {
+ LOGGER.info("Connection closed");
+
+ if (!status.equals(CloseStatus.NORMAL)) {
+ try {
+ session.close();
+ }
+ catch (IOException e) {
+ LOGGER.error("Failed to close session on afterConnectionClosed ", e);
+ }
+ }
+ sessions.remove(session);
+ webSocketService.connectToSocket();
+ }
+}
diff --git a/backend/src/main/java/ru/micord/ervu/account_applications/websocket/service/WebSocketService.java b/backend/src/main/java/ru/micord/ervu/account_applications/websocket/service/WebSocketService.java
new file mode 100644
index 00000000..93f3de98
--- /dev/null
+++ b/backend/src/main/java/ru/micord/ervu/account_applications/websocket/service/WebSocketService.java
@@ -0,0 +1,51 @@
+package ru.micord.ervu.account_applications.websocket.service;
+
+import java.net.URI;
+import java.util.concurrent.ExecutionException;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+import org.springframework.web.socket.WebSocketHandler;
+import org.springframework.web.socket.WebSocketHttpHeaders;
+import org.springframework.web.socket.client.WebSocketClient;
+
+/**
+ * @author gulnaz
+ */
+@Service
+public class WebSocketService {
+ private static final Logger LOGGER = LoggerFactory.getLogger(WebSocketService.class);
+
+ private final WebSocketClient webSocketClient;
+ private final WebSocketHandler webSocketHandler;
+
+ @Value("${ervu.socket.url:wss://ervu-uat.test.gosuslugi.ru/service/notifier/gateway/notify/notifier.message.send.push}")
+ private String socketUrl;
+
+ public WebSocketService(WebSocketClient webSocketClient,
+ WebSocketHandler webSocketHandler) {
+ this.webSocketClient = webSocketClient;
+ this.webSocketHandler = webSocketHandler;
+ }
+
+ public void connectToSocket() {
+ WebSocketHttpHeaders headers = new WebSocketHttpHeaders();
+ headers.set("Content-Type", "application/json");
+// headers.add("Authorization", "Bearer " + securityContext.getToken());
+ headers.add("Authorization", "Bearer " + getToken());
+
+ try {
+ webSocketClient.doHandshake(webSocketHandler, headers, URI.create(socketUrl)).get();
+ }
+ catch (InterruptedException | ExecutionException e) {
+ LOGGER.error("Failed to connect socket");
+ }
+ }
+
+ private String getToken() {
+ return
+ "eyJraWQiOiJzc29rZXkxIiwidHlwIjoiSldUIiwiYWxnIjoiUlM1MTIifQ.eyJzdWIiOiI3MDhjMGJkNS05MDk0LTQyZjUtYWQwZS0xZWUyMTBlZTM5MDMiLCJhbXIiOlsiSURQX0xPQ0FMIl0sInJvbGVzIjpbInNlY3VyaXR5X2FkbWluaXN0cmF0b3IiLCLQkdCw0LfQvtCy0LDRjyDRgNC-0LvRjCDQtNC70Y8g0LDQstGC0L7RgNC40LfQsNGG0LjQuCIsItCf0L7Qu9GM0LfQvtCy0LDRgtC10LvRjNGB0LrQsNGPINGA0L7Qu9GMIiwiUzNDbGllbnQiXSwiaXNzIjoiaHR0cHM6Ly9lcnZ1LXVhdC1zc28udGVzdC5nb3N1c2x1Z2kucnUiLCJncm91cHMiOltdLCJkb21haW5faWQiOiI1NTNiOTQ0OS02NDYwLTQ4YzQtODhkNC1mMTgyODNhYmM0YTciLCJhdWQiOiJhcm0iLCJhY2NvdW50SWQiOiI3MDhjMGJkNS05MDk0LTQyZjUtYWQwZS0xZWUyMTBlZTM5MDMiLCJuYmYiOjAsImF6cCI6ImFybSIsImF1dGhfdGltZSI6MCwibmFtZSI6ItCX0LDRgNC40L_QvtCyINCt0LzQuNC70YwiLCJyZWFsbSI6ItCS0L7RgdGM0LzQvtC1INGD0L_RgNCw0LLQu9C10L3QuNC1INCT0Kgg0JLQoSDQoNCkIiwicG9zaXRpb24iOiLQnNC40LrQvtGA0LQg0J_QntCY0JEiLCJleHAiOjE3NDE4MjM4NDgsInNlc3Npb25fc3RhdGUiOiI4M2UyMzdmNS05NzQzLTQ4ZmMtYjY2MS1jN2ZhODc1NjcyNDQiLCJpYXQiOjE3NDE4MDk0NDgsIlVTRVJfSVAiOiIxNzIuMjYuMjcuMTAifQ.em1QK3Tux2WUCxk-ii0I6VApr7KKsKcnWaM7mPfS2T3VEyn9pwZuLo9KQsIaQ1kIf-xsdz3iwuWruyHn0KxUlJQB_s7gAcfLMPy_2XVwydb--XviwvZ9ZRpQbdb_uhoAojhO9_h_UGuQOaKwF3K_g4EfgyWnD9xHyrujLlV9HIoipH6Eixci9jhscelWXv76wP8sbyPyeB9YrKnWTtFyhCvMb3y3FjwSCDs7Hi7JPh0SDQUQ-o8z0h6mOPIuoo53S03AEVr40f3s9nr0APq7HoU3UW3kQfkEI9A060pqO3c2ItnJU5-FE-og0wMVEnEvXeZtHebsSP3IdssJDpKrNg";
+ }
+}
diff --git a/pom.xml b/pom.xml
index ff92feb8..082d4a7f 100644
--- a/pom.xml
+++ b/pom.xml
@@ -16,6 +16,7 @@
UTF-8
false
+ 5.3.39
2.9.2
3.192.3
72000
@@ -286,6 +287,16 @@
${webbpm-platform.version}
runtime
+
+ org.springframework
+ spring-websocket
+ ${spring.version}
+
+
+ org.springframework
+ spring-messaging
+ ${spring.version}
+
javax.validation
validation-api