Добавил поле authNonce(Вместо SessionPWD) при запросе авторизации
This commit is contained in:
AidarKC 2025-12-11 16:22:12 +03:00
parent dbf1f22bac
commit 80ffba545a
4 changed files with 37 additions and 31 deletions

View File

@ -27,11 +27,16 @@ public class ConnectionContext {
private String sessionId; private String sessionId;
/** /**
* Временный секрет шага 1, который используется на шаге 2 и хранится в БД, * Секрет сессии (то, что хранится в active_sessions.session_pwd).
* а после успешной авторизации настоящий секрет сессии.
*/ */
private String sessionPwd; private String sessionPwd;
/**
* Одноразовый nonce, выданный на шаге 1 (AuthChallenge),
* используется на шаге 2 для проверки подписи.
*/
private String authNonce;
/** /**
* Текущий статус аутентификации. * Текущий статус аутентификации.
* См. константы AUTH_STATUS_* * См. константы AUTH_STATUS_*
@ -100,6 +105,16 @@ public class ConnectionContext {
this.sessionPwd = sessionPwd; this.sessionPwd = sessionPwd;
} }
// --- authNonce ---
public String getAuthNonce() {
return authNonce;
}
public void setAuthNonce(String authNonce) {
this.authNonce = authNonce;
}
// --- auth status --- // --- auth status ---
public int getAuthenticationStatus() { public int getAuthenticationStatus() {
@ -124,6 +139,7 @@ public class ConnectionContext {
sessionId = null; sessionId = null;
sessionPwd = null; sessionPwd = null;
authNonce = null;
authenticationStatus = AUTH_STATUS_NONE; authenticationStatus = AUTH_STATUS_NONE;
wsSession = null; wsSession = null;

View File

@ -3,9 +3,9 @@ package server.logic.ws_protocol.JSON.entyties.Auth;
import server.logic.ws_protocol.JSON.entyties.Net_Request; import server.logic.ws_protocol.JSON.entyties.Net_Request;
/** /**
* Шаг 1 авторизации: запрос выдачи временного пароля сессии (sessionPwd). * Шаг 1 авторизации: запрос выдачи одноразового nonce (authNonce).
* *
* Клиент по логину просит сервер сгенерировать случайный секрет sessionPwd, * Клиент по логину просит сервер сгенерировать случайный authNonce,
* который будет использован на втором шаге при подписи. * который будет использован на втором шаге при подписи.
* *
* Формат входящего JSON: * Формат входящего JSON:
@ -23,7 +23,7 @@ import server.logic.ws_protocol.JSON.entyties.Net_Request;
* "requestId": "...", * "requestId": "...",
* "status": 200, * "status": 200,
* "payload": { * "payload": {
* "sessionPwd": "base64-строка-от-32-байт" * "authNonce": "base64-строка-от-32-байт"
* } * }
* } * }
*/ */
@ -40,4 +40,4 @@ public class Net_AuthChallenge_Request extends Net_Request {
public void setLogin(String login) { public void setLogin(String login) {
this.login = login; this.login = login;
} }
} }

View File

@ -71,8 +71,8 @@ public class Net_AuthChallenge_Handler implements JsonMessageHandler {
RANDOM.nextBytes(buf); RANDOM.nextBytes(buf);
String authNonce = Base64.getUrlEncoder().withoutPadding().encodeToString(buf); String authNonce = Base64.getUrlEncoder().withoutPadding().encodeToString(buf);
// Используем поле sessionPwd в контексте как хранилище challenge (authNonce) до шага 2 // Сохраняем challenge в отдельном поле authNonce
ctx.setSessionPwd(authNonce); ctx.setAuthNonce(authNonce);
// 5) Формируем ответ // 5) Формируем ответ
Net_AuthChallenge_Response resp = new Net_AuthChallenge_Response(); Net_AuthChallenge_Response resp = new Net_AuthChallenge_Response();

View File

@ -35,7 +35,8 @@ import java.util.Base64;
* "AUTHORIFICATED:" + timeMs + authNonce) * "AUTHORIFICATED:" + timeMs + authNonce)
* - clientInfo (опционально, до 50 символов) * - clientInfo (опционально, до 50 символов)
* *
* authNonce клиент получил на шаге 1 (AuthChallenge). * authNonce клиент получил на шаге 1 (AuthChallenge) и сервер
* сохранил его в ctx.authNonce.
* *
* При успехе: * При успехе:
* - создаётся запись ActiveSession в БД; * - создаётся запись ActiveSession в БД;
@ -59,27 +60,19 @@ public class Net_CreateAuthSession__Handler implements JsonMessageHandler {
public Net_Response handle(Net_Request baseReq, ConnectionContext ctx) throws Exception { public Net_Response handle(Net_Request baseReq, ConnectionContext ctx) throws Exception {
Net_CreateAuthSession_Request req = (Net_CreateAuthSession_Request) baseReq; Net_CreateAuthSession_Request req = (Net_CreateAuthSession_Request) baseReq;
// --- базовые проверки контекста --- // --- базовые проверки контекста шага 1 ---
if (ctx == null || ctx.getSolanaUser() == null || ctx.getSessionPwd() == null) { if (ctx == null
|| ctx.getSolanaUser() == null
|| ctx.getAuthNonce() == null
|| ctx.getAuthenticationStatus() != ConnectionContext.AUTH_STATUS_AUTH_IN_PROGRESS) {
Net_Response err = NetExceptionResponseFactory.error( Net_Response err = NetExceptionResponseFactory.error(
req, req,
WireCodes.Status.BAD_REQUEST, WireCodes.Status.BAD_REQUEST,
"NO_STEP1_CONTEXT", "NO_STEP1_CONTEXT",
"Шаг 1 авторизации не был корректно выполнен для данного соединения" "Шаг 1 авторизации не был корректно выполнен для данного соединения"
); );
WsConnectionUtils.closeConnection(ctx, 4001, "Auth failed: no step1 context"); WsConnectionUtils.closeConnection(ctx, 4001, "Auth failed: no step1 context or bad auth state");
return err;
}
// Ожидаем, что перед этим был AuthChallenge и статус = AUTH_IN_PROGRESS
if (ctx.getAuthenticationStatus() != ConnectionContext.AUTH_STATUS_AUTH_IN_PROGRESS) {
Net_Response err = NetExceptionResponseFactory.error(
req,
WireCodes.Status.BAD_REQUEST,
"BAD_AUTH_FLOW_STATE",
"Неожиданное состояние авторификации для данного соединения"
);
WsConnectionUtils.closeConnection(ctx, 4001, "Auth failed: bad auth flow state");
return err; return err;
} }
@ -170,8 +163,8 @@ public class Net_CreateAuthSession__Handler implements JsonMessageHandler {
return err; return err;
} }
// --- authNonce (challenge) мы сохранили в ctx.sessionPwd на шаге 1 --- // --- authNonce (challenge) мы сохранили в ctx.authNonce на шаге 1 ---
String authNonce = ctx.getSessionPwd(); String authNonce = ctx.getAuthNonce();
// --- собираем строку для подписи: "AUTHORIFICATED:" + timeMs + authNonce --- // --- собираем строку для подписи: "AUTHORIFICATED:" + timeMs + authNonce ---
String preimageStr = "AUTHORIFICATED:" + timeMs + authNonce; String preimageStr = "AUTHORIFICATED:" + timeMs + authNonce;
@ -201,16 +194,12 @@ public class Net_CreateAuthSession__Handler implements JsonMessageHandler {
String clientIp = null; String clientIp = null;
if (wsSession != null) { if (wsSession != null) {
// стандартный метод получения IP
clientIp = ClientInfoService.extractClientIp(wsSession); clientIp = ClientInfoService.extractClientIp(wsSession);
// Дёргаем запрос геолокации (ничего не сохраняем в сессию),
// нужно лишь для того, чтобы данные попали в кэш сервера.
if (clientIp != null && !clientIp.isBlank()) { if (clientIp != null && !clientIp.isBlank()) {
try { try {
GeoLookupService.resolveCountryCityOrIpWithCache(clientIp); GeoLookupService.resolveCountryCityOrIpWithCache(clientIp);
} catch (Exception e) { } catch (Exception e) {
// геолокация не критична, можно тихо залогировать на debug
log.debug("Geo lookup failed for ip={}", clientIp, e); log.debug("Geo lookup failed for ip={}", clientIp, e);
} }
} }
@ -257,7 +246,8 @@ public class Net_CreateAuthSession__Handler implements JsonMessageHandler {
// --- обновляем контекст --- // --- обновляем контекст ---
ctx.setActiveSession(activeSession); ctx.setActiveSession(activeSession);
ctx.setSessionId(sessionId); ctx.setSessionId(sessionId);
ctx.setSessionPwd(newSessionPwd); // теперь в контексте хранится секрет сессии, а не authNonce ctx.setSessionPwd(newSessionPwd); // теперь в контексте хранится секрет сессии
ctx.setAuthNonce(null); // одноразовый nonce больше не нужен
ctx.setAuthenticationStatus(ConnectionContext.AUTH_STATUS_USER); ctx.setAuthenticationStatus(ConnectionContext.AUTH_STATUS_USER);
ActiveConnectionsRegistry.getInstance().register(ctx); ActiveConnectionsRegistry.getInstance().register(ctx);