09 12 25
В черновую переделал авторификацию
This commit is contained in:
parent
2b5fa16824
commit
2ed4f6d666
@ -5,8 +5,7 @@ import shine.db.entities.ActiveSession;
|
|||||||
|
|
||||||
import java.sql.*;
|
import java.sql.*;
|
||||||
|
|
||||||
/** Здесь мы хрним данные об активных сессиях пользователя (для wss соединений) */
|
/** Здесь мы храним данные об активных сессиях пользователя (для wss соединений). */
|
||||||
|
|
||||||
public final class ActiveSessionsDAO {
|
public final class ActiveSessionsDAO {
|
||||||
|
|
||||||
private static volatile ActiveSessionsDAO instance;
|
private static volatile ActiveSessionsDAO instance;
|
||||||
@ -30,37 +29,40 @@ public final class ActiveSessionsDAO {
|
|||||||
String sql = """
|
String sql = """
|
||||||
INSERT INTO active_sessions (
|
INSERT INTO active_sessions (
|
||||||
sessionId,
|
sessionId,
|
||||||
session_pwd,
|
|
||||||
loginId,
|
loginId,
|
||||||
time_ms,
|
session_pwd,
|
||||||
pubkey_num,
|
storage_pwd,
|
||||||
|
session_created_ms,
|
||||||
|
last_auth_ms,
|
||||||
push_endpoint,
|
push_endpoint,
|
||||||
push_p256dh_key,
|
push_p256dh_key,
|
||||||
push_auth_key
|
push_auth_key
|
||||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||||
""";
|
""";
|
||||||
|
|
||||||
try (PreparedStatement ps = db.getConnection().prepareStatement(sql)) {
|
try (PreparedStatement ps = db.getConnection().prepareStatement(sql)) {
|
||||||
ps.setLong(1, session.getSessionId());
|
ps.setString(1, session.getSessionId());
|
||||||
ps.setString(2, session.getSessionPwd());
|
ps.setLong(2, session.getLoginId());
|
||||||
ps.setLong(3, session.getLoginId());
|
ps.setString(3, session.getSessionPwd());
|
||||||
ps.setLong(4, session.getTimeMs());
|
ps.setString(4, session.getStoragePwd());
|
||||||
ps.setInt(5, session.getPubkeyNum());
|
ps.setLong(5, session.getSessionCreatedAtMs());
|
||||||
ps.setString(6, session.getPushEndpoint());
|
ps.setLong(6, session.getLastAuthirificatedAtMs());
|
||||||
ps.setString(7, session.getPushP256dhKey());
|
ps.setString(7, session.getPushEndpoint());
|
||||||
ps.setString(8, session.getPushAuthKey());
|
ps.setString(8, session.getPushP256dhKey());
|
||||||
|
ps.setString(9, session.getPushAuthKey());
|
||||||
ps.executeUpdate();
|
ps.executeUpdate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public ActiveSession getBySessionId(long sessionId) throws SQLException {
|
public ActiveSession getBySessionId(String sessionId) throws SQLException {
|
||||||
String sql = """
|
String sql = """
|
||||||
SELECT
|
SELECT
|
||||||
sessionId,
|
sessionId,
|
||||||
session_pwd,
|
|
||||||
loginId,
|
loginId,
|
||||||
time_ms,
|
session_pwd,
|
||||||
pubkey_num,
|
storage_pwd,
|
||||||
|
session_created_ms,
|
||||||
|
last_auth_ms,
|
||||||
push_endpoint,
|
push_endpoint,
|
||||||
push_p256dh_key,
|
push_p256dh_key,
|
||||||
push_auth_key
|
push_auth_key
|
||||||
@ -69,7 +71,7 @@ public final class ActiveSessionsDAO {
|
|||||||
""";
|
""";
|
||||||
|
|
||||||
try (PreparedStatement ps = db.getConnection().prepareStatement(sql)) {
|
try (PreparedStatement ps = db.getConnection().prepareStatement(sql)) {
|
||||||
ps.setLong(1, sessionId);
|
ps.setString(1, sessionId);
|
||||||
try (ResultSet rs = ps.executeQuery()) {
|
try (ResultSet rs = ps.executeQuery()) {
|
||||||
if (!rs.next()) {
|
if (!rs.next()) {
|
||||||
return null;
|
return null;
|
||||||
@ -83,31 +85,51 @@ public final class ActiveSessionsDAO {
|
|||||||
* Удаление записи по sessionId.
|
* Удаление записи по sessionId.
|
||||||
* Если записи нет — просто ничего не удалит (0 строк).
|
* Если записи нет — просто ничего не удалит (0 строк).
|
||||||
*/
|
*/
|
||||||
public void deleteBySessionId(long sessionId) throws SQLException {
|
public void deleteBySessionId(String sessionId) throws SQLException {
|
||||||
String sql = "DELETE FROM active_sessions WHERE sessionId = ?";
|
String sql = "DELETE FROM active_sessions WHERE sessionId = ?";
|
||||||
|
|
||||||
try (PreparedStatement ps = db.getConnection().prepareStatement(sql)) {
|
try (PreparedStatement ps = db.getConnection().prepareStatement(sql)) {
|
||||||
ps.setLong(1, sessionId);
|
ps.setString(1, sessionId);
|
||||||
|
ps.executeUpdate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Обновить поле last_auth_ms (lastAuthirificatedAtMs) для конкретной сессии.
|
||||||
|
* Остальные поля записи не меняются.
|
||||||
|
*/
|
||||||
|
public void updateLastAuthirificatedAtMs(String sessionId, long newTimeMs) throws SQLException {
|
||||||
|
String sql = """
|
||||||
|
UPDATE active_sessions
|
||||||
|
SET last_auth_ms = ?
|
||||||
|
WHERE sessionId = ?
|
||||||
|
""";
|
||||||
|
|
||||||
|
try (PreparedStatement ps = db.getConnection().prepareStatement(sql)) {
|
||||||
|
ps.setLong(1, newTimeMs);
|
||||||
|
ps.setString(2, sessionId);
|
||||||
ps.executeUpdate();
|
ps.executeUpdate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private ActiveSession mapRow(ResultSet rs) throws SQLException {
|
private ActiveSession mapRow(ResultSet rs) throws SQLException {
|
||||||
long sessionId = rs.getLong("sessionId");
|
String sessionId = rs.getString("sessionId");
|
||||||
String sessionPwd = rs.getString("session_pwd");
|
|
||||||
long loginId = rs.getLong("loginId");
|
long loginId = rs.getLong("loginId");
|
||||||
long timeMs = rs.getLong("time_ms");
|
String sessionPwd = rs.getString("session_pwd");
|
||||||
short pubkeyNum = (short) rs.getInt("pubkey_num");
|
String storagePwd = rs.getString("storage_pwd");
|
||||||
|
long sessionCreatedMs = rs.getLong("session_created_ms");
|
||||||
|
long lastAuthMs = rs.getLong("last_auth_ms");
|
||||||
String pushEndpoint = rs.getString("push_endpoint");
|
String pushEndpoint = rs.getString("push_endpoint");
|
||||||
String pushP256dhKey = rs.getString("push_p256dh_key");
|
String pushP256dhKey = rs.getString("push_p256dh_key");
|
||||||
String pushAuthKey = rs.getString("push_auth_key");
|
String pushAuthKey = rs.getString("push_auth_key");
|
||||||
|
|
||||||
return new ActiveSession(
|
return new ActiveSession(
|
||||||
sessionId,
|
sessionId,
|
||||||
sessionPwd,
|
|
||||||
loginId,
|
loginId,
|
||||||
timeMs,
|
sessionPwd,
|
||||||
pubkeyNum,
|
storagePwd,
|
||||||
|
sessionCreatedMs,
|
||||||
|
lastAuthMs,
|
||||||
pushEndpoint,
|
pushEndpoint,
|
||||||
pushP256dhKey,
|
pushP256dhKey,
|
||||||
pushAuthKey
|
pushAuthKey
|
||||||
|
|||||||
@ -1,12 +1,27 @@
|
|||||||
package shine.db.entities;
|
package shine.db.entities;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ActiveSession — запись об активной сессии пользователя.
|
||||||
|
*
|
||||||
|
* Поля:
|
||||||
|
* - sessionId – строка (base64 от 32 байт)
|
||||||
|
* - loginId – long
|
||||||
|
* - sessionPwd – строка (секрет шага 1)
|
||||||
|
* - storagePwd – строка (секрет клиента для хранения данных)
|
||||||
|
* - sessionCreatedAtMs – long (время создания)
|
||||||
|
* - lastAuthirificatedAtMs – long (последнее подтверждение/refresh)
|
||||||
|
* - pushEndpoint – строка (WebPush, пока null/пусто)
|
||||||
|
* - pushP256dhKey – строка (WebPush, пока null/пусто)
|
||||||
|
* - pushAuthKey – строка (WebPush, пока null/пусто)
|
||||||
|
*/
|
||||||
public class ActiveSession {
|
public class ActiveSession {
|
||||||
|
|
||||||
private long sessionId;
|
private String sessionId;
|
||||||
private String sessionPwd;
|
|
||||||
private long loginId;
|
private long loginId;
|
||||||
private long timeMs; // время в мс
|
private String sessionPwd;
|
||||||
private short pubkeyNum;
|
private String storagePwd;
|
||||||
|
private long sessionCreatedAtMs;
|
||||||
|
private long lastAuthirificatedAtMs;
|
||||||
private String pushEndpoint;
|
private String pushEndpoint;
|
||||||
private String pushP256dhKey;
|
private String pushP256dhKey;
|
||||||
private String pushAuthKey;
|
private String pushAuthKey;
|
||||||
@ -14,68 +29,71 @@ public class ActiveSession {
|
|||||||
public ActiveSession() {
|
public ActiveSession() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public ActiveSession(long sessionId,
|
public ActiveSession(String sessionId,
|
||||||
String sessionPwd,
|
|
||||||
long loginId,
|
long loginId,
|
||||||
long timeMs,
|
String sessionPwd,
|
||||||
short pubkeyNum,
|
String storagePwd,
|
||||||
|
long sessionCreatedAtMs,
|
||||||
|
long lastAuthirificatedAtMs,
|
||||||
String pushEndpoint,
|
String pushEndpoint,
|
||||||
String pushP256dhKey,
|
String pushP256dhKey,
|
||||||
String pushAuthKey) {
|
String pushAuthKey) {
|
||||||
this.sessionId = sessionId;
|
this.sessionId = sessionId;
|
||||||
this.sessionPwd = sessionPwd;
|
|
||||||
this.loginId = loginId;
|
this.loginId = loginId;
|
||||||
this.timeMs = timeMs;
|
this.sessionPwd = sessionPwd;
|
||||||
this.pubkeyNum = pubkeyNum;
|
this.storagePwd = storagePwd;
|
||||||
|
this.sessionCreatedAtMs = sessionCreatedAtMs;
|
||||||
|
this.lastAuthirificatedAtMs = lastAuthirificatedAtMs;
|
||||||
this.pushEndpoint = pushEndpoint;
|
this.pushEndpoint = pushEndpoint;
|
||||||
this.pushP256dhKey = pushP256dhKey;
|
this.pushP256dhKey = pushP256dhKey;
|
||||||
this.pushAuthKey = pushAuthKey;
|
this.pushAuthKey = pushAuthKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getSessionId() {
|
public String getSessionId() {
|
||||||
return sessionId;
|
return sessionId;
|
||||||
}
|
}
|
||||||
|
public void setSessionId(String sessionId) {
|
||||||
public void setSessionId(long sessionId) {
|
|
||||||
this.sessionId = sessionId;
|
this.sessionId = sessionId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getSessionPwd() {
|
|
||||||
return sessionPwd;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setSessionPwd(String sessionPwd) {
|
|
||||||
this.sessionPwd = sessionPwd;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getLoginId() {
|
public long getLoginId() {
|
||||||
return loginId;
|
return loginId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setLoginId(long loginId) {
|
public void setLoginId(long loginId) {
|
||||||
this.loginId = loginId;
|
this.loginId = loginId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getTimeMs() {
|
public String getSessionPwd() {
|
||||||
return timeMs;
|
return sessionPwd;
|
||||||
|
}
|
||||||
|
public void setSessionPwd(String sessionPwd) {
|
||||||
|
this.sessionPwd = sessionPwd;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setTimeMs(long timeMs) {
|
public String getStoragePwd() {
|
||||||
this.timeMs = timeMs;
|
return storagePwd;
|
||||||
|
}
|
||||||
|
public void setStoragePwd(String storagePwd) {
|
||||||
|
this.storagePwd = storagePwd;
|
||||||
}
|
}
|
||||||
|
|
||||||
public short getPubkeyNum() {
|
public long getSessionCreatedAtMs() {
|
||||||
return pubkeyNum;
|
return sessionCreatedAtMs;
|
||||||
|
}
|
||||||
|
public void setSessionCreatedAtMs(long sessionCreatedAtMs) {
|
||||||
|
this.sessionCreatedAtMs = sessionCreatedAtMs;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setPubkeyNum(short pubkeyNum) {
|
public long getLastAuthirificatedAtMs() {
|
||||||
this.pubkeyNum = pubkeyNum;
|
return lastAuthirificatedAtMs;
|
||||||
|
}
|
||||||
|
public void setLastAuthirificatedAtMs(long lastAuthirificatedAtMs) {
|
||||||
|
this.lastAuthirificatedAtMs = lastAuthirificatedAtMs;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getPushEndpoint() {
|
public String getPushEndpoint() {
|
||||||
return pushEndpoint;
|
return pushEndpoint;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setPushEndpoint(String pushEndpoint) {
|
public void setPushEndpoint(String pushEndpoint) {
|
||||||
this.pushEndpoint = pushEndpoint;
|
this.pushEndpoint = pushEndpoint;
|
||||||
}
|
}
|
||||||
@ -83,7 +101,6 @@ public class ActiveSession {
|
|||||||
public String getPushP256dhKey() {
|
public String getPushP256dhKey() {
|
||||||
return pushP256dhKey;
|
return pushP256dhKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setPushP256dhKey(String pushP256dhKey) {
|
public void setPushP256dhKey(String pushP256dhKey) {
|
||||||
this.pushP256dhKey = pushP256dhKey;
|
this.pushP256dhKey = pushP256dhKey;
|
||||||
}
|
}
|
||||||
@ -91,7 +108,6 @@ public class ActiveSession {
|
|||||||
public String getPushAuthKey() {
|
public String getPushAuthKey() {
|
||||||
return pushAuthKey;
|
return pushAuthKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setPushAuthKey(String pushAuthKey) {
|
public void setPushAuthKey(String pushAuthKey) {
|
||||||
this.pushAuthKey = pushAuthKey;
|
this.pushAuthKey = pushAuthKey;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,20 +6,19 @@ import java.util.concurrent.CopyOnWriteArraySet;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Реестр активных подключений (только авторизованные).
|
* Реестр активных подключений (только авторизованные).
|
||||||
*.
|
*
|
||||||
* Позволяет:
|
* Позволяет:
|
||||||
* - получить ConnectionContext по sessionId;
|
* - получить ConnectionContext по sessionId;
|
||||||
* - получить все активные подключения пользователя по loginId;
|
* - получить все активные подключения пользователя по loginId;
|
||||||
* - удалить подключение при закрытии WebSocket.
|
* - удалить подключение при закрытии WebSocket.
|
||||||
*.
|
*
|
||||||
* найти все подключения пользователя:
|
* найти все подключения пользователя:
|
||||||
* var set = ActiveConnectionsRegistry.getInstance().getByLoginId(loginId);
|
* var set = ActiveConnectionsRegistry.getInstance().getByLoginId(loginId);
|
||||||
*.
|
*
|
||||||
* найти конкретное подключение по sessionId:
|
* найти конкретное подключение по sessionId:
|
||||||
* ConnectionContext ctx = ActiveConnectionsRegistry.getInstance().getBySessionId(sessionId);
|
* ConnectionContext ctx = ActiveConnectionsRegistry.getInstance().getBySessionId(sessionId);
|
||||||
* Session ws = ctx != null ? ctx.getWsSession() : null;
|
* Session ws = ctx != null ? ctx.getWsSession() : null;
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public final class ActiveConnectionsRegistry {
|
public final class ActiveConnectionsRegistry {
|
||||||
|
|
||||||
private static final ActiveConnectionsRegistry INSTANCE = new ActiveConnectionsRegistry();
|
private static final ActiveConnectionsRegistry INSTANCE = new ActiveConnectionsRegistry();
|
||||||
@ -32,8 +31,8 @@ public final class ActiveConnectionsRegistry {
|
|||||||
// singleton
|
// singleton
|
||||||
}
|
}
|
||||||
|
|
||||||
// sessionId -> ConnectionContext
|
// sessionId (String) -> ConnectionContext
|
||||||
private final ConcurrentHashMap<Long, ConnectionContext> bySessionId = new ConcurrentHashMap<>();
|
private final ConcurrentHashMap<String, ConnectionContext> bySessionId = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
// loginId -> множество ConnectionContext для этого пользователя
|
// loginId -> множество ConnectionContext для этого пользователя
|
||||||
private final ConcurrentHashMap<Long, Set<ConnectionContext>> byLoginId = new ConcurrentHashMap<>();
|
private final ConcurrentHashMap<Long, Set<ConnectionContext>> byLoginId = new ConcurrentHashMap<>();
|
||||||
@ -45,7 +44,7 @@ public final class ActiveConnectionsRegistry {
|
|||||||
public void register(ConnectionContext ctx) {
|
public void register(ConnectionContext ctx) {
|
||||||
if (ctx == null) return;
|
if (ctx == null) return;
|
||||||
|
|
||||||
Long sessionId = ctx.getSessionId();
|
String sessionId = ctx.getSessionId();
|
||||||
Long loginId = ctx.getLoginId();
|
Long loginId = ctx.getLoginId();
|
||||||
|
|
||||||
if (sessionId == null || loginId == null) {
|
if (sessionId == null || loginId == null) {
|
||||||
@ -65,7 +64,7 @@ public final class ActiveConnectionsRegistry {
|
|||||||
public void remove(ConnectionContext ctx) {
|
public void remove(ConnectionContext ctx) {
|
||||||
if (ctx == null) return;
|
if (ctx == null) return;
|
||||||
|
|
||||||
Long sessionId = ctx.getSessionId();
|
String sessionId = ctx.getSessionId();
|
||||||
Long loginId = ctx.getLoginId();
|
Long loginId = ctx.getLoginId();
|
||||||
|
|
||||||
if (sessionId != null) {
|
if (sessionId != null) {
|
||||||
@ -86,7 +85,9 @@ public final class ActiveConnectionsRegistry {
|
|||||||
/**
|
/**
|
||||||
* Удалить подключение по sessionId.
|
* Удалить подключение по sessionId.
|
||||||
*/
|
*/
|
||||||
public void removeBySessionId(long sessionId) {
|
public void removeBySessionId(String sessionId) {
|
||||||
|
if (sessionId == null) return;
|
||||||
|
|
||||||
ConnectionContext ctx = bySessionId.remove(sessionId);
|
ConnectionContext ctx = bySessionId.remove(sessionId);
|
||||||
if (ctx != null) {
|
if (ctx != null) {
|
||||||
Long loginId = ctx.getLoginId();
|
Long loginId = ctx.getLoginId();
|
||||||
@ -105,7 +106,8 @@ public final class ActiveConnectionsRegistry {
|
|||||||
/**
|
/**
|
||||||
* Получить контекст по sessionId.
|
* Получить контекст по sessionId.
|
||||||
*/
|
*/
|
||||||
public ConnectionContext getBySessionId(long sessionId) {
|
public ConnectionContext getBySessionId(String sessionId) {
|
||||||
|
if (sessionId == null) return null;
|
||||||
return bySessionId.get(sessionId);
|
return bySessionId.get(sessionId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -20,7 +20,14 @@ public class ConnectionContext {
|
|||||||
// Активная сессия из БД (active_sessions)
|
// Активная сессия из БД (active_sessions)
|
||||||
private ActiveSession activeSession;
|
private ActiveSession activeSession;
|
||||||
|
|
||||||
private Long sessionId;
|
/**
|
||||||
|
* Идентификатор сессии — base64-строка от 32 байт.
|
||||||
|
*/
|
||||||
|
private String sessionId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Временный секрет шага 1, который используется на шаге 2 и хранится в БД.
|
||||||
|
*/
|
||||||
private String sessionPwd;
|
private String sessionPwd;
|
||||||
|
|
||||||
private int authenticationStatus = AUTH_STATUS_NONE;
|
private int authenticationStatus = AUTH_STATUS_NONE;
|
||||||
@ -71,11 +78,11 @@ public class ConnectionContext {
|
|||||||
|
|
||||||
// --- sessionId / sessionPwd ---
|
// --- sessionId / sessionPwd ---
|
||||||
|
|
||||||
public Long getSessionId() {
|
public String getSessionId() {
|
||||||
return sessionId;
|
return sessionId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setSessionId(Long sessionId) {
|
public void setSessionId(String sessionId) {
|
||||||
this.sessionId = sessionId;
|
this.sessionId = sessionId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -2,7 +2,36 @@ package server.logic.ws_protocol.JSON.entyties.Auth;
|
|||||||
|
|
||||||
import server.logic.ws_protocol.JSON.entyties.NetRequest;
|
import server.logic.ws_protocol.JSON.entyties.NetRequest;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Шаг 1 авторизации: запрос выдачи временного пароля сессии (sessionPwd).
|
||||||
|
*
|
||||||
|
* Клиент по логину просит сервер сгенерировать случайный секрет sessionPwd,
|
||||||
|
* который будет использован на втором шаге при подписи.
|
||||||
|
*
|
||||||
|
* Формат входящего JSON:
|
||||||
|
* {
|
||||||
|
* "op": "AuthSessionNewStep1",
|
||||||
|
* "requestId": "...",
|
||||||
|
* "payload": {
|
||||||
|
* "login": "someLogin"
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* Формат успешного ответа:
|
||||||
|
* {
|
||||||
|
* "op": "AuthSessionNewStep1",
|
||||||
|
* "requestId": "...",
|
||||||
|
* "status": 200,
|
||||||
|
* "payload": {
|
||||||
|
* "sessionPwd": "base64-строка-от-32-байт"
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
*/
|
||||||
public class NetAuthSessionNewStep1Request extends NetRequest {
|
public class NetAuthSessionNewStep1Request extends NetRequest {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Логин пользователя, для которого запускается авторизация.
|
||||||
|
*/
|
||||||
private String login;
|
private String login;
|
||||||
|
|
||||||
public String getLogin() {
|
public String getLogin() {
|
||||||
|
|||||||
@ -2,7 +2,28 @@ package server.logic.ws_protocol.JSON.entyties.Auth;
|
|||||||
|
|
||||||
import server.logic.ws_protocol.JSON.entyties.NetResponse;
|
import server.logic.ws_protocol.JSON.entyties.NetResponse;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ответ на AuthSessionNewStep1.
|
||||||
|
*
|
||||||
|
* При успехе сервер возвращает временный секрет sessionPwd,
|
||||||
|
* который клиент обязан использовать на втором шаге при формировании подписи.
|
||||||
|
*
|
||||||
|
* JSON:
|
||||||
|
* {
|
||||||
|
* "op": "AuthSessionNewStep1",
|
||||||
|
* "requestId": "...",
|
||||||
|
* "status": 200,
|
||||||
|
* "payload": {
|
||||||
|
* "sessionPwd": "base64-строка-от-32-байт"
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
*/
|
||||||
public class NetAuthSessionNewStep1Response extends NetResponse {
|
public class NetAuthSessionNewStep1Response extends NetResponse {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Временный секрет, сгенерированный сервером.
|
||||||
|
* Строка — это base64-представление 32 случайных байт.
|
||||||
|
*/
|
||||||
private String sessionPwd;
|
private String sessionPwd;
|
||||||
|
|
||||||
public String getSessionPwd() {
|
public String getSessionPwd() {
|
||||||
|
|||||||
@ -3,39 +3,47 @@ package server.logic.ws_protocol.JSON.entyties.Auth;
|
|||||||
import server.logic.ws_protocol.JSON.entyties.NetRequest;
|
import server.logic.ws_protocol.JSON.entyties.NetRequest;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Шаг 2 авторизации: клиент подтверждает владение ключом.
|
* Шаг 2 авторизации: подтверждение владения ключом и установка сессии.
|
||||||
*.
|
*
|
||||||
* JSON:
|
* Клиент:
|
||||||
|
* 1) получает от сервера sessionPwd на шаге 1;
|
||||||
|
* 2) генерирует свой StoragePwd (base64 от 32 байт);
|
||||||
|
* 3) формирует строку для подписи:
|
||||||
|
* "AUTHORIFICATED:" + timeMs + sessionPwd
|
||||||
|
* 4) подписывает эту строку своим приватным ключом (pubkey1),
|
||||||
|
* отправляет подпись и StoragePwd на сервер.
|
||||||
|
*
|
||||||
|
* Формат входящего JSON:
|
||||||
* {
|
* {
|
||||||
* "op": "AuthSessionNewStep2",
|
* "op": "AuthSessionNewStep2",
|
||||||
* "requestId": "...",
|
* "requestId": "...",
|
||||||
* "loginId": 100211,
|
* "payload": {
|
||||||
* "sigNum": 0, // номер подписи: 0 или 1
|
* "storagePwd": "base64-строка-от-32-байт",
|
||||||
* "timeMs": 1733310000000, // время в миллисекундах с 1970-01-01
|
* "timeMs": 1733310000000,
|
||||||
* "signatureB64": "..." // подпись base64 от строки loginId+timeMs+sessionPwd
|
* "signatureB64": "base64-подпись-Ed25519"
|
||||||
* }
|
* }
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* При успешной проверке подписи сервер создаёт запись в active_sessions
|
||||||
|
* и возвращает sessionId (base64-строка от 32 байт).
|
||||||
*/
|
*/
|
||||||
public class NetAuthSessionNewStep2Request extends NetRequest {
|
public class NetAuthSessionNewStep2Request extends NetRequest {
|
||||||
|
|
||||||
private long loginId;
|
/** Клиентский пароль для хранения данных (base64 от 32 байт). */
|
||||||
private int sigNum; // 0 или 1
|
private String storagePwd;
|
||||||
private long timeMs; // миллисекунды с 1970
|
|
||||||
|
/** Время на стороне клиента (мс с 1970-01-01). */
|
||||||
|
private long timeMs;
|
||||||
|
|
||||||
|
/** Подпись Ed25519 над строкой "AUTHORIFICATED:" + timeMs + sessionPwd (base64). */
|
||||||
private String signatureB64;
|
private String signatureB64;
|
||||||
|
|
||||||
public long getLoginId() {
|
public String getStoragePwd() {
|
||||||
return loginId;
|
return storagePwd;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setLoginId(long loginId) {
|
public void setStoragePwd(String storagePwd) {
|
||||||
this.loginId = loginId;
|
this.storagePwd = storagePwd;
|
||||||
}
|
|
||||||
|
|
||||||
public int getSigNum() {
|
|
||||||
return sigNum;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setSigNum(int sigNum) {
|
|
||||||
this.sigNum = sigNum;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getTimeMs() {
|
public long getTimeMs() {
|
||||||
|
|||||||
@ -4,26 +4,30 @@ import server.logic.ws_protocol.JSON.entyties.NetResponse;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Ответ на AuthSessionNewStep2.
|
* Ответ на AuthSessionNewStep2.
|
||||||
*.
|
*
|
||||||
* Успешный JSON:
|
* При успехе сервер создаёт запись в active_sessions
|
||||||
|
* и возвращает идентификатор сессии sessionId.
|
||||||
|
*
|
||||||
|
* JSON:
|
||||||
* {
|
* {
|
||||||
* "op": "AuthSessionNewStep2",
|
* "op": "AuthSessionNewStep2",
|
||||||
* "requestId": "...",
|
* "requestId": "...",
|
||||||
* "status": 200,
|
* "status": 200,
|
||||||
* "payload": {
|
* "payload": {
|
||||||
* "sessionId": 1234567890
|
* "sessionId": "base64-строка-от-32-байт"
|
||||||
* }
|
* }
|
||||||
* }
|
* }
|
||||||
*/
|
*/
|
||||||
public class NetAuthSessionNewStep2Response extends NetResponse {
|
public class NetAuthSessionNewStep2Response extends NetResponse {
|
||||||
|
|
||||||
private Long sessionId;
|
/** Идентификатор сессии, base64 от 32 байт. */
|
||||||
|
private String sessionId;
|
||||||
|
|
||||||
public Long getSessionId() {
|
public String getSessionId() {
|
||||||
return sessionId;
|
return sessionId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setSessionId(Long sessionId) {
|
public void setSessionId(String sessionId) {
|
||||||
this.sessionId = sessionId;
|
this.sessionId = sessionId;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,23 +4,26 @@ import server.logic.ws_protocol.JSON.entyties.NetRequest;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Запрос SessionRefresh.
|
* Запрос SessionRefresh.
|
||||||
*.
|
*
|
||||||
|
* Используется для повторного входа без повторной подписи:
|
||||||
|
* клиент хранит sessionId и sessionPwd, которые получил на шаге 2.
|
||||||
|
*
|
||||||
* JSON (payload):
|
* JSON (payload):
|
||||||
* {
|
* {
|
||||||
* "sessionId": 123,
|
* "sessionId": "base64-id-сессии",
|
||||||
* "sessionPwd": "abcd..."
|
* "sessionPwd": "base64-sessionPwd"
|
||||||
* }
|
* }
|
||||||
*/
|
*/
|
||||||
public class NetSessionRefreshRequest extends NetRequest {
|
public class NetSessionRefreshRequest extends NetRequest {
|
||||||
|
|
||||||
private long sessionId;
|
private String sessionId;
|
||||||
private String sessionPwd;
|
private String sessionPwd;
|
||||||
|
|
||||||
public long getSessionId() {
|
public String getSessionId() {
|
||||||
return sessionId;
|
return sessionId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setSessionId(long sessionId) {
|
public void setSessionId(String sessionId) {
|
||||||
this.sessionId = sessionId;
|
this.sessionId = sessionId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -4,9 +4,30 @@ import server.logic.ws_protocol.JSON.entyties.NetResponse;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Успешный ответ на SessionRefresh.
|
* Успешный ответ на SessionRefresh.
|
||||||
*.
|
*
|
||||||
* Дополнительных полей нет, достаточно status=200 и (опционально) пустого payload.
|
* Дополнительно к статусу 200 сервер возвращает storagePwd,
|
||||||
|
* чтобы клиент мог восстановить/синхронизировать локальное хранилище.
|
||||||
|
*
|
||||||
|
* JSON:
|
||||||
|
* {
|
||||||
|
* "op": "SessionRefresh",
|
||||||
|
* "requestId": "...",
|
||||||
|
* "status": 200,
|
||||||
|
* "payload": {
|
||||||
|
* "storagePwd": "base64-строка-от-32-байт"
|
||||||
|
* }
|
||||||
|
* }
|
||||||
*/
|
*/
|
||||||
public class NetSessionRefreshResponse extends NetResponse {
|
public class NetSessionRefreshResponse extends NetResponse {
|
||||||
// Ничего дополнительного, вся информация в status.
|
|
||||||
|
/** Пароль хранилища, сохранённый в сессии (storagePwd). */
|
||||||
|
private String storagePwd;
|
||||||
|
|
||||||
|
public String getStoragePwd() {
|
||||||
|
return storagePwd;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStoragePwd(String storagePwd) {
|
||||||
|
this.storagePwd = storagePwd;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,6 +11,7 @@ import shine.db.dao.SolanaUsersDAO;
|
|||||||
import shine.db.entities.SolanaUser;
|
import shine.db.entities.SolanaUser;
|
||||||
|
|
||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
|
import java.util.Base64;
|
||||||
|
|
||||||
public class NetAuthSessionNewStep1Handler implements JsonMessageHandler {
|
public class NetAuthSessionNewStep1Handler implements JsonMessageHandler {
|
||||||
|
|
||||||
@ -57,9 +58,10 @@ public class NetAuthSessionNewStep1Handler implements JsonMessageHandler {
|
|||||||
// 3) Заполняем контекст целиком пользователем
|
// 3) Заполняем контекст целиком пользователем
|
||||||
ctx.setSolanaUser(solanaUser);
|
ctx.setSolanaUser(solanaUser);
|
||||||
|
|
||||||
// 4) Генерируем надёжный sessionPwd
|
// 4) Генерируем надёжный sessionPwd = base64(32 случайных байт)
|
||||||
String sessionPwd = Long.toHexString(System.nanoTime()) +
|
byte[] buf = new byte[32];
|
||||||
Long.toHexString(RANDOM.nextLong());
|
RANDOM.nextBytes(buf);
|
||||||
|
String sessionPwd = Base64.getUrlEncoder().withoutPadding().encodeToString(buf);
|
||||||
|
|
||||||
ctx.setSessionPwd(sessionPwd);
|
ctx.setSessionPwd(sessionPwd);
|
||||||
|
|
||||||
@ -68,7 +70,7 @@ public class NetAuthSessionNewStep1Handler implements JsonMessageHandler {
|
|||||||
resp.setOp(req.getOp());
|
resp.setOp(req.getOp());
|
||||||
resp.setRequestId(req.getRequestId());
|
resp.setRequestId(req.getRequestId());
|
||||||
resp.setStatus(WireCodes.Status.OK);
|
resp.setStatus(WireCodes.Status.OK);
|
||||||
resp.setSessionPwd(sessionPwd); // 🔴 Больше не трогаем payload
|
resp.setSessionPwd(sessionPwd);
|
||||||
|
|
||||||
return resp;
|
return resp;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -18,22 +18,38 @@ import utils.crypto.Ed25519Util;
|
|||||||
|
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
|
import java.security.SecureRandom;
|
||||||
import java.util.Base64;
|
import java.util.Base64;
|
||||||
import java.util.concurrent.ThreadLocalRandom;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Шаг 2 авторизации: проверка подписи и создание сессии.
|
* Шаг 2 авторизации: проверка подписи и создание сессии.
|
||||||
*.
|
*
|
||||||
* Клиент присылает:
|
* Клиент присылает в payload:
|
||||||
* - loginId
|
* - storagePwd (base64 от 32 байт)
|
||||||
* - sigNum (0 или 1)
|
* - timeMs (long, мс с 1970-01-01)
|
||||||
* - timeMs
|
* - signatureB64 (подпись Ed25519 над строкой:
|
||||||
* - signatureB64 от строки (loginId + timeMs + sessionPwd)
|
* "AUTHORIFICATED:" + timeMs + sessionPwd)
|
||||||
|
*
|
||||||
|
* Параметр sessionPwd клиент получил на шаге 1.
|
||||||
|
* Для проверки подписи используется pubkey1 (второй публичный ключ пользователя).
|
||||||
|
*
|
||||||
|
* Дополнительно:
|
||||||
|
* - timeMs должен отличаться от текущего времени сервера не более чем на 30 секунд.
|
||||||
|
*
|
||||||
|
* При успехе:
|
||||||
|
* - создаётся запись ActiveSession в БД;
|
||||||
|
* - генерируется sessionId (base64 от 32 случайных байт);
|
||||||
|
* - sessionCreatedAtMs и lastAuthirificatedAtMs = текущее время;
|
||||||
|
* - pushEndpoint / pushP256dhKey / pushAuthKey остаются пустыми;
|
||||||
|
* - возвращается sessionId в ответе.
|
||||||
*/
|
*/
|
||||||
public class NetAuthSessionNewStep2Handler implements JsonMessageHandler {
|
public class NetAuthSessionNewStep2Handler implements JsonMessageHandler {
|
||||||
|
|
||||||
private static final Logger log = LoggerFactory.getLogger(NetAuthSessionNewStep2Handler.class);
|
private static final Logger log = LoggerFactory.getLogger(NetAuthSessionNewStep2Handler.class);
|
||||||
|
|
||||||
|
private static final SecureRandom RANDOM = new SecureRandom();
|
||||||
|
private static final long ALLOWED_SKEW_MS = 30_000L;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public NetResponse handle(NetRequest baseReq, ConnectionContext ctx) throws Exception {
|
public NetResponse handle(NetRequest baseReq, ConnectionContext ctx) throws Exception {
|
||||||
NetAuthSessionNewStep2Request req = (NetAuthSessionNewStep2Request) baseReq;
|
NetAuthSessionNewStep2Request req = (NetAuthSessionNewStep2Request) baseReq;
|
||||||
@ -58,25 +74,23 @@ public class NetAuthSessionNewStep2Handler implements JsonMessageHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
SolanaUser user = ctx.getSolanaUser();
|
SolanaUser user = ctx.getSolanaUser();
|
||||||
long reqLoginId = req.getLoginId();
|
Long loginId = user.getLoginId();
|
||||||
Long ctxLoginId = user.getLoginId();
|
if (loginId == null) {
|
||||||
|
|
||||||
if (ctxLoginId == null || ctxLoginId != reqLoginId) {
|
|
||||||
return NetExceptionResponseFactory.error(
|
return NetExceptionResponseFactory.error(
|
||||||
req,
|
req,
|
||||||
WireCodes.Status.UNVERIFIED,
|
WireCodes.Status.SERVER_DATA_ERROR,
|
||||||
"LOGIN_ID_MISMATCH",
|
"NO_LOGIN_ID",
|
||||||
"loginId в запросе не совпадает с пользователем из шага 1"
|
"Для пользователя не задан loginId в БД"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
int sigNum = req.getSigNum();
|
String storagePwd = req.getStoragePwd();
|
||||||
if (sigNum != 0 && sigNum != 1) {
|
if (storagePwd == null || storagePwd.isBlank()) {
|
||||||
return NetExceptionResponseFactory.error(
|
return NetExceptionResponseFactory.error(
|
||||||
req,
|
req,
|
||||||
WireCodes.Status.BAD_REQUEST,
|
WireCodes.Status.BAD_REQUEST,
|
||||||
"BAD_SIG_NUM",
|
"EMPTY_STORAGE_PWD",
|
||||||
"Номер подписи должен быть 0 или 1"
|
"Пустой storagePwd"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -90,14 +104,28 @@ public class NetAuthSessionNewStep2Handler implements JsonMessageHandler {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- выбираем публичный ключ по sigNum ---
|
long timeMs = req.getTimeMs();
|
||||||
String pubKeyB64 = (sigNum == 0) ? user.getPubkey0() : user.getPubkey1();
|
long nowMs = System.currentTimeMillis();
|
||||||
|
|
||||||
|
// Проверка, что время клиента не отличается от времени сервера больше чем на 30 секунд
|
||||||
|
long diff = Math.abs(nowMs - timeMs);
|
||||||
|
if (diff > ALLOWED_SKEW_MS) {
|
||||||
|
return NetExceptionResponseFactory.error(
|
||||||
|
req,
|
||||||
|
WireCodes.Status.BAD_REQUEST,
|
||||||
|
"TIME_SKEW",
|
||||||
|
"Время клиента отличается от сервера более чем на 30 секунд"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- выбираем публичный ключ pubkey1 ---
|
||||||
|
String pubKeyB64 = user.getPubkey1();
|
||||||
if (pubKeyB64 == null || pubKeyB64.isBlank()) {
|
if (pubKeyB64 == null || pubKeyB64.isBlank()) {
|
||||||
return NetExceptionResponseFactory.error(
|
return NetExceptionResponseFactory.error(
|
||||||
req,
|
req,
|
||||||
WireCodes.Status.BAD_REQUEST,
|
WireCodes.Status.BAD_REQUEST,
|
||||||
"NO_PUBKEY",
|
"NO_PUBKEY1",
|
||||||
"Отсутствует публичный ключ для выбранного номера подписи"
|
"Отсутствует публичный ключ pubkey1 для пользователя"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -115,9 +143,8 @@ public class NetAuthSessionNewStep2Handler implements JsonMessageHandler {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- собираем строку для подписи: loginId + timeMs + sessionPwd ---
|
// --- собираем строку для подписи: "AUTHORIFICATED:" + timeMs + sessionPwd ---
|
||||||
long timeMs = req.getTimeMs();
|
String preimageStr = "AUTHORIFICATED:" + timeMs + ctx.getSessionPwd();
|
||||||
String preimageStr = String.valueOf(reqLoginId) + timeMs + ctx.getSessionPwd();
|
|
||||||
byte[] preimage = preimageStr.getBytes(StandardCharsets.UTF_8);
|
byte[] preimage = preimageStr.getBytes(StandardCharsets.UTF_8);
|
||||||
|
|
||||||
boolean sigOk = Ed25519Util.verify(preimage, signature64, publicKey32);
|
boolean sigOk = Ed25519Util.verify(preimage, signature64, publicKey32);
|
||||||
@ -130,21 +157,22 @@ public class NetAuthSessionNewStep2Handler implements JsonMessageHandler {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- создаём уникальный sessionId и записываем в БД ---
|
// --- создаём уникальный sessionId (base64 от 32 байт) и записываем в БД ---
|
||||||
ActiveSessionsDAO dao = ActiveSessionsDAO.getInstance();
|
ActiveSessionsDAO dao = ActiveSessionsDAO.getInstance();
|
||||||
long sessionId;
|
String sessionId;
|
||||||
ActiveSession activeSession;
|
ActiveSession activeSession;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
sessionId = generateUniqueSessionId(dao);
|
sessionId = generateRandomSessionId();
|
||||||
long nowMs = System.currentTimeMillis();
|
long now = System.currentTimeMillis();
|
||||||
|
|
||||||
activeSession = new ActiveSession(
|
activeSession = new ActiveSession(
|
||||||
sessionId,
|
sessionId,
|
||||||
|
loginId,
|
||||||
ctx.getSessionPwd(),
|
ctx.getSessionPwd(),
|
||||||
reqLoginId,
|
storagePwd,
|
||||||
nowMs,
|
now,
|
||||||
(short) sigNum, // pubkeyNum
|
now,
|
||||||
null, // pushEndpoint
|
null, // pushEndpoint
|
||||||
null, // pushP256dhKey
|
null, // pushP256dhKey
|
||||||
null // pushAuthKey
|
null // pushAuthKey
|
||||||
@ -152,7 +180,7 @@ public class NetAuthSessionNewStep2Handler implements JsonMessageHandler {
|
|||||||
|
|
||||||
dao.insert(activeSession);
|
dao.insert(activeSession);
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
log.error("Ошибка БД при создании новой сессии для loginId={}", reqLoginId, e);
|
log.error("Ошибка БД при создании новой сессии для loginId={}", loginId, e);
|
||||||
return NetExceptionResponseFactory.error(
|
return NetExceptionResponseFactory.error(
|
||||||
req,
|
req,
|
||||||
WireCodes.Status.SERVER_DATA_ERROR,
|
WireCodes.Status.SERVER_DATA_ERROR,
|
||||||
@ -166,7 +194,6 @@ public class NetAuthSessionNewStep2Handler implements JsonMessageHandler {
|
|||||||
ctx.setSessionId(sessionId);
|
ctx.setSessionId(sessionId);
|
||||||
ctx.setAuthenticationStatus(ConnectionContext.AUTH_STATUS_USER);
|
ctx.setAuthenticationStatus(ConnectionContext.AUTH_STATUS_USER);
|
||||||
|
|
||||||
ActiveConnectionsRegistry.getInstance().removeBySessionId(sessionId); // га всякий случай предварительно удаляем что бы точно небыло дублирования активной сессии
|
|
||||||
// Регистрируем это подключение в глобальном реестре активных соединений
|
// Регистрируем это подключение в глобальном реестре активных соединений
|
||||||
ActiveConnectionsRegistry.getInstance().register(ctx);
|
ActiveConnectionsRegistry.getInstance().register(ctx);
|
||||||
|
|
||||||
@ -180,16 +207,11 @@ public class NetAuthSessionNewStep2Handler implements JsonMessageHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Генерация уникального sessionId с проверкой в БД.
|
* Генерация случайного sessionId: base64-строка от 32 байт.
|
||||||
*/
|
*/
|
||||||
private long generateUniqueSessionId(ActiveSessionsDAO dao) throws SQLException {
|
private String generateRandomSessionId() {
|
||||||
for (int i = 0; i < 10; i++) {
|
byte[] buf = new byte[32];
|
||||||
long candidate = ThreadLocalRandom.current().nextLong(Long.MAX_VALUE);
|
RANDOM.nextBytes(buf);
|
||||||
ActiveSession existing = dao.getBySessionId(candidate);
|
return Base64.getUrlEncoder().withoutPadding().encodeToString(buf);
|
||||||
if (existing == null) {
|
|
||||||
return candidate;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
throw new SQLException("Не удалось сгенерировать уникальный sessionId за разумное число попыток");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,6 +20,12 @@ import java.sql.SQLException;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Хэндлер SessionRefresh.
|
* Хэндлер SessionRefresh.
|
||||||
|
*
|
||||||
|
* При успешной проверке sessionId + sessionPwd:
|
||||||
|
* - подтягивает пользователя по loginId из сессии;
|
||||||
|
* - заполняет ConnectionContext;
|
||||||
|
* - обновляет lastAuthirificatedAtMs в БД на текущее время;
|
||||||
|
* - возвращает storagePwd в payload.
|
||||||
*/
|
*/
|
||||||
public class NetSessionRefreshHandler implements JsonMessageHandler {
|
public class NetSessionRefreshHandler implements JsonMessageHandler {
|
||||||
|
|
||||||
@ -29,9 +35,18 @@ public class NetSessionRefreshHandler implements JsonMessageHandler {
|
|||||||
public NetResponse handle(NetRequest request, ConnectionContext ctx) throws Exception {
|
public NetResponse handle(NetRequest request, ConnectionContext ctx) throws Exception {
|
||||||
NetSessionRefreshRequest req = (NetSessionRefreshRequest) request;
|
NetSessionRefreshRequest req = (NetSessionRefreshRequest) request;
|
||||||
|
|
||||||
long sessionId = req.getSessionId();
|
String sessionId = req.getSessionId();
|
||||||
String sessionPwd = req.getSessionPwd();
|
String sessionPwd = req.getSessionPwd();
|
||||||
|
|
||||||
|
if (sessionId == null || sessionId.isBlank()) {
|
||||||
|
return NetExceptionResponseFactory.error(
|
||||||
|
req,
|
||||||
|
WireCodes.Status.BAD_REQUEST,
|
||||||
|
"BAD_SESSION_ID",
|
||||||
|
"Пустой идентификатор сессии"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (sessionPwd == null || sessionPwd.isEmpty()) {
|
if (sessionPwd == null || sessionPwd.isEmpty()) {
|
||||||
return NetExceptionResponseFactory.error(
|
return NetExceptionResponseFactory.error(
|
||||||
req,
|
req,
|
||||||
@ -76,13 +91,10 @@ public class NetSessionRefreshHandler implements JsonMessageHandler {
|
|||||||
|
|
||||||
// --- достаём пользователя по loginId из сессии ---
|
// --- достаём пользователя по loginId из сессии ---
|
||||||
SolanaUser solanaUser = null;
|
SolanaUser solanaUser = null;
|
||||||
Long loginId = null;
|
long loginId = session.getLoginId();
|
||||||
try {
|
try {
|
||||||
loginId = session.getLoginId();
|
|
||||||
if (loginId != null) {
|
|
||||||
SolanaUsersDAO usersDao = SolanaUsersDAO.getInstance();
|
SolanaUsersDAO usersDao = SolanaUsersDAO.getInstance();
|
||||||
solanaUser = usersDao.getByLoginId(loginId);
|
solanaUser = usersDao.getByLoginId(loginId);
|
||||||
}
|
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
log.error("Ошибка БД при поиске пользователя по loginId={} из сессии", loginId, e);
|
log.error("Ошибка БД при поиске пользователя по loginId={} из сессии", loginId, e);
|
||||||
return NetExceptionResponseFactory.error(
|
return NetExceptionResponseFactory.error(
|
||||||
@ -93,7 +105,7 @@ public class NetSessionRefreshHandler implements JsonMessageHandler {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (loginId != null && solanaUser == null) {
|
if (solanaUser == null) {
|
||||||
return NetExceptionResponseFactory.error(
|
return NetExceptionResponseFactory.error(
|
||||||
req,
|
req,
|
||||||
WireCodes.Status.UNVERIFIED,
|
WireCodes.Status.UNVERIFIED,
|
||||||
@ -110,16 +122,24 @@ public class NetSessionRefreshHandler implements JsonMessageHandler {
|
|||||||
ctx.setSessionPwd(sessionPwd);
|
ctx.setSessionPwd(sessionPwd);
|
||||||
ctx.setAuthenticationStatus(ConnectionContext.AUTH_STATUS_USER);
|
ctx.setAuthenticationStatus(ConnectionContext.AUTH_STATUS_USER);
|
||||||
|
|
||||||
ActiveConnectionsRegistry.getInstance().removeBySessionId(sessionId); // на всякий случай удаляем что бы точно небыло повторов
|
|
||||||
// Регистрируем это подключение в глобальном реестре активных соединений
|
// Регистрируем это подключение в глобальном реестре активных соединений
|
||||||
ActiveConnectionsRegistry.getInstance().register(ctx);
|
ActiveConnectionsRegistry.getInstance().register(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
// И возвращаем OK без доп. полей (payload будет {}).
|
// Обновляем lastAuthirificatedAtMs в БД
|
||||||
|
try {
|
||||||
|
long nowMs = System.currentTimeMillis();
|
||||||
|
sessionsDao.updateLastAuthirificatedAtMs(sessionId, nowMs);
|
||||||
|
} catch (SQLException e) {
|
||||||
|
log.error("Ошибка БД при обновлении lastAuthirificatedAtMs для sessionId={}", sessionId, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Возвращаем OK + storagePwd
|
||||||
NetSessionRefreshResponse resp = new NetSessionRefreshResponse();
|
NetSessionRefreshResponse resp = new NetSessionRefreshResponse();
|
||||||
resp.setOp(req.getOp());
|
resp.setOp(req.getOp());
|
||||||
resp.setRequestId(req.getRequestId());
|
resp.setRequestId(req.getRequestId());
|
||||||
resp.setStatus(WireCodes.Status.OK);
|
resp.setStatus(WireCodes.Status.OK);
|
||||||
|
resp.setStoragePwd(session.getStoragePwd());
|
||||||
return resp;
|
return resp;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user