Авторификация работает и тест авторификации проходит.

(создание пользователя, два этапа создания сессии и рефреш сессии)
This commit is contained in:
AidarKC
2025-12-09 20:04:18 +03:00
parent 2ed4f6d666
commit 888bb1595f
12 changed files with 723 additions and 372 deletions
@@ -1,6 +1,5 @@
package shine.db;
import utils.config.AppConfig;
import java.io.BufferedReader;
@@ -12,6 +11,15 @@ import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
/**
* DatabaseInitializer — создание новой SQLite-БД по схеме SHiNE.
*
* Читает путь к файлу БД из application.properties (db.path),
* при необходимости удаляет старый файл и создаёт таблицы:
* - solana_users
* - active_sessions
* - users_params
*/
public class DatabaseInitializer {
public static void createNewDB(String[] args) {
@@ -75,9 +83,9 @@ public class DatabaseInitializer {
login TEXT NOT NULL,
loginId INTEGER NOT NULL PRIMARY KEY,
bchId INTEGER NOT NULL,
pubkey0 TEXT,
pubkey1 TEXT,
bchLimit INTEGER -- может быть NULL
loginKey TEXT, -- основной публичный ключ (логин)
deviceKey TEXT, -- публичный ключ устройства
bchLimit INTEGER -- может быть NULL
);
""");
@@ -87,22 +95,29 @@ public class DatabaseInitializer {
""");
// 2. Таблица active_sessions
// sessionId теперь TEXT (base64 от 32 байт), а не INTEGER.
st.executeUpdate("""
CREATE TABLE IF NOT EXISTS active_sessions (
sessionId INTEGER NOT NULL PRIMARY KEY,
session_pwd TEXT NOT NULL,
loginId INTEGER NOT NULL,
time_ms INTEGER NOT NULL,
pubkey_num INTEGER NOT NULL,
push_endpoint TEXT,
push_p256dh_key TEXT,
push_auth_key TEXT,
sessionId TEXT NOT NULL PRIMARY KEY,
loginId INTEGER NOT NULL,
sessionPwd TEXT NOT NULL,
storagePwd TEXT NOT NULL,
sessionCreatedAtMs INTEGER NOT NULL,
lastAuthirificatedAtMs INTEGER NOT NULL,
pushEndpoint TEXT,
pushP256dhKey TEXT,
pushAuthKey TEXT,
FOREIGN KEY (loginId) REFERENCES solana_users(loginId)
);
""");
st.executeUpdate("""
CREATE INDEX IF NOT EXISTS idx_active_sessions_loginId
ON active_sessions (loginId);
""");
// 3. Таблица users_params
// Важно: пара (loginId, param) должна быть уникальна
// Пара (loginId, param) должна быть уникальна.
st.executeUpdate("""
CREATE TABLE IF NOT EXISTS users_params (
loginId INTEGER NOT NULL,
@@ -5,7 +5,11 @@ import shine.db.entities.ActiveSession;
import java.sql.*;
/** Здесь мы храним данные об активных сессиях пользователя (для wss соединений). */
/**
* DAO для таблицы active_sessions.
*
* Здесь мы храним данные об активных сессиях пользователя (для wss-соединений).
*/
public final class ActiveSessionsDAO {
private static volatile ActiveSessionsDAO instance;
@@ -25,18 +29,21 @@ public final class ActiveSessionsDAO {
return instance;
}
/**
* Вставка новой сессии.
*/
public void insert(ActiveSession session) throws SQLException {
String sql = """
INSERT INTO active_sessions (
sessionId,
loginId,
session_pwd,
storage_pwd,
session_created_ms,
last_auth_ms,
push_endpoint,
push_p256dh_key,
push_auth_key
sessionPwd,
storagePwd,
sessionCreatedAtMs,
lastAuthirificatedAtMs,
pushEndpoint,
pushP256dhKey,
pushAuthKey
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
""";
@@ -50,22 +57,26 @@ public final class ActiveSessionsDAO {
ps.setString(7, session.getPushEndpoint());
ps.setString(8, session.getPushP256dhKey());
ps.setString(9, session.getPushAuthKey());
ps.executeUpdate();
}
}
/**
* Получить сессию по sessionId.
*/
public ActiveSession getBySessionId(String sessionId) throws SQLException {
String sql = """
SELECT
sessionId,
loginId,
session_pwd,
storage_pwd,
session_created_ms,
last_auth_ms,
push_endpoint,
push_p256dh_key,
push_auth_key
sessionPwd,
storagePwd,
sessionCreatedAtMs,
lastAuthirificatedAtMs,
pushEndpoint,
pushP256dhKey,
pushAuthKey
FROM active_sessions
WHERE sessionId = ?
""";
@@ -81,6 +92,23 @@ public final class ActiveSessionsDAO {
}
}
/**
* Обновить только lastAuthirificatedAtMs для конкретной сессии.
*/
public void updateLastAuthirificatedAtMs(String sessionId, long lastAuthMs) throws SQLException {
String sql = """
UPDATE active_sessions
SET lastAuthirificatedAtMs = ?
WHERE sessionId = ?
""";
try (PreparedStatement ps = db.getConnection().prepareStatement(sql)) {
ps.setLong(1, lastAuthMs);
ps.setString(2, sessionId);
ps.executeUpdate();
}
}
/**
* Удаление записи по sessionId.
* Если записи нет — просто ничего не удалит (0 строк).
@@ -94,42 +122,24 @@ public final class ActiveSessionsDAO {
}
}
/**
* Обновить поле 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();
}
}
private ActiveSession mapRow(ResultSet rs) throws SQLException {
String sessionId = rs.getString("sessionId");
long loginId = rs.getLong("loginId");
String sessionPwd = rs.getString("session_pwd");
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 pushP256dhKey = rs.getString("push_p256dh_key");
String pushAuthKey = rs.getString("push_auth_key");
String sessionPwd = rs.getString("sessionPwd");
String storagePwd = rs.getString("storagePwd");
long sessionCreatedAtMs = rs.getLong("sessionCreatedAtMs");
long lastAuthirificatedAtMs = rs.getLong("lastAuthirificatedAtMs");
String pushEndpoint = rs.getString("pushEndpoint");
String pushP256dhKey = rs.getString("pushP256dhKey");
String pushAuthKey = rs.getString("pushAuthKey");
return new ActiveSession(
sessionId,
loginId,
sessionPwd,
storagePwd,
sessionCreatedMs,
lastAuthMs,
sessionCreatedAtMs,
lastAuthirificatedAtMs,
pushEndpoint,
pushP256dhKey,
pushAuthKey
@@ -7,8 +7,17 @@ import java.sql.*;
import java.util.ArrayList;
import java.util.List;
/** Здесь храним данные об пользователях - локальная копия того что есть в солане */
/**
* SolanaUsersDAO — локальная таблица пользователей из Solana.
*
* Колонки:
* - login TEXT
* - loginId INTEGER (PK)
* - bchId INTEGER
* - loginKey TEXT
* - deviceKey TEXT
* - bchLimit INTEGER (может быть NULL)
*/
public final class SolanaUsersDAO {
private static volatile SolanaUsersDAO instance;
@@ -29,7 +38,7 @@ public final class SolanaUsersDAO {
public void insert(SolanaUser user) throws SQLException {
String sql = """
INSERT INTO solana_users (login, loginId, bchId, pubkey0, pubkey1, bchLimit)
INSERT INTO solana_users (login, loginId, bchId, loginKey, deviceKey, bchLimit)
VALUES (?, ?, ?, ?, ?, ?)
""";
@@ -37,8 +46,8 @@ public final class SolanaUsersDAO {
ps.setString(1, user.getLogin());
ps.setLong(2, user.getLoginId());
ps.setLong(3, user.getBchId());
ps.setString(4, user.getPubkey0());
ps.setString(5, user.getPubkey1());
ps.setString(4, user.getLoginKey());
ps.setString(5, user.getDeviceKey());
if (user.getBchLimit() != null) {
ps.setInt(6, user.getBchLimit());
@@ -52,7 +61,7 @@ public final class SolanaUsersDAO {
public SolanaUser getByLoginId(long loginId) throws SQLException {
String sql = """
SELECT login, loginId, bchId, pubkey0, pubkey1, bchLimit
SELECT login, loginId, bchId, loginKey, deviceKey, bchLimit
FROM solana_users
WHERE loginId = ?
""";
@@ -69,7 +78,7 @@ public final class SolanaUsersDAO {
public SolanaUser getByLogin(String login) throws SQLException {
String sql = """
SELECT login, loginId, bchId, pubkey0, pubkey1, bchLimit
SELECT login, loginId, bchId, loginKey, deviceKey, bchLimit
FROM solana_users
WHERE LOWER(login) = LOWER(?)
""";
@@ -86,7 +95,7 @@ public final class SolanaUsersDAO {
public List<SolanaUser> searchByLoginPrefix(String prefix) throws SQLException {
String sql = """
SELECT login, loginId, bchId, pubkey0, pubkey1, bchLimit
SELECT login, loginId, bchId, loginKey, deviceKey, bchLimit
FROM solana_users
WHERE LOWER(login) LIKE ?
ORDER BY login
@@ -111,8 +120,8 @@ public final class SolanaUsersDAO {
rs.getLong("loginId"),
rs.getString("login"),
rs.getLong("bchId"),
rs.getString("pubkey0"),
rs.getString("pubkey1"),
rs.getString("loginKey"),
rs.getString("deviceKey"),
rs.getObject("bchLimit") != null ? rs.getInt("bchLimit") : null
);
}
@@ -1,30 +1,34 @@
package shine.db.entities;
/**
* ActiveSession — запись об активной сессии пользователя.
* Модель активной сессии (таблица active_sessions).
*
* Поля:
* - sessionId строка (base64 от 32 байт)
* - loginId long
* - sessionPwd – строка (секрет шага 1)
* - storagePwd – строка (секрет клиента для хранения данных)
* - sessionCreatedAtMs long (время создания)
* - lastAuthirificatedAtMs long (последнее подтверждение/refresh)
* - pushEndpoint строка (WebPush, пока null/пусто)
* - pushP256dhKey строка (WebPush, пока null/пусто)
* - pushAuthKey строка (WebPush, пока null/пусто)
* Поля соответствуют схеме:
*
* CREATE TABLE active_sessions (
* sessionId TEXT NOT NULL PRIMARY KEY,
* loginId INTEGER NOT NULL,
* sessionPwd TEXT NOT NULL,
* storagePwd TEXT NOT NULL,
* sessionCreatedAtMs INTEGER NOT NULL,
* lastAuthirificatedAtMs INTEGER NOT NULL,
* pushEndpoint TEXT,
* pushP256dhKey TEXT,
* pushAuthKey TEXT,
* FOREIGN KEY (loginId) REFERENCES solana_users(loginId)
* );
*/
public class ActiveSession {
private String sessionId;
private long loginId;
private String sessionPwd;
private String storagePwd;
private long sessionCreatedAtMs;
private long lastAuthirificatedAtMs;
private String pushEndpoint;
private String pushP256dhKey;
private String pushAuthKey;
private String sessionId; // TEXT base64(32 bytes)
private long loginId; // INTEGER
private String sessionPwd; // TEXT
private String storagePwd; // TEXT
private long sessionCreatedAtMs; // INTEGER
private long lastAuthirificatedAtMs; // INTEGER
private String pushEndpoint; // TEXT (nullable)
private String pushP256dhKey; // TEXT (nullable)
private String pushAuthKey; // TEXT (nullable)
public ActiveSession() {
}
@@ -49,9 +53,12 @@ public class ActiveSession {
this.pushAuthKey = pushAuthKey;
}
// --- getters / setters ---
public String getSessionId() {
return sessionId;
}
public void setSessionId(String sessionId) {
this.sessionId = sessionId;
}
@@ -59,6 +66,7 @@ public class ActiveSession {
public long getLoginId() {
return loginId;
}
public void setLoginId(long loginId) {
this.loginId = loginId;
}
@@ -66,6 +74,7 @@ public class ActiveSession {
public String getSessionPwd() {
return sessionPwd;
}
public void setSessionPwd(String sessionPwd) {
this.sessionPwd = sessionPwd;
}
@@ -73,6 +82,7 @@ public class ActiveSession {
public String getStoragePwd() {
return storagePwd;
}
public void setStoragePwd(String storagePwd) {
this.storagePwd = storagePwd;
}
@@ -80,6 +90,7 @@ public class ActiveSession {
public long getSessionCreatedAtMs() {
return sessionCreatedAtMs;
}
public void setSessionCreatedAtMs(long sessionCreatedAtMs) {
this.sessionCreatedAtMs = sessionCreatedAtMs;
}
@@ -87,6 +98,7 @@ public class ActiveSession {
public long getLastAuthirificatedAtMs() {
return lastAuthirificatedAtMs;
}
public void setLastAuthirificatedAtMs(long lastAuthirificatedAtMs) {
this.lastAuthirificatedAtMs = lastAuthirificatedAtMs;
}
@@ -94,6 +106,7 @@ public class ActiveSession {
public String getPushEndpoint() {
return pushEndpoint;
}
public void setPushEndpoint(String pushEndpoint) {
this.pushEndpoint = pushEndpoint;
}
@@ -101,6 +114,7 @@ public class ActiveSession {
public String getPushP256dhKey() {
return pushP256dhKey;
}
public void setPushP256dhKey(String pushP256dhKey) {
this.pushP256dhKey = pushP256dhKey;
}
@@ -108,6 +122,7 @@ public class ActiveSession {
public String getPushAuthKey() {
return pushAuthKey;
}
public void setPushAuthKey(String pushAuthKey) {
this.pushAuthKey = pushAuthKey;
}
@@ -1,13 +1,23 @@
package shine.db.entities;
/**
* Локальная копия пользователя из Solana.
*
* Храним:
* - login / loginId;
* - bchId — id персонального блокчейна;
* - loginKey — публичный ключ для логина / авторизации;
* - deviceKey — публичный ключ устройства (второй ключ);
* - bchLimit — лимит по количеству блоков / размеру цепочки (может быть null).
*/
public class SolanaUser {
private long loginId;
private String login;
private long bchId;
private String pubkey0;
private String pubkey1;
private Integer bchLimit; // может быть null
private String loginKey; // раньше pubkey0
private String deviceKey; // раньше pubkey1
private Integer bchLimit; // может быть null
public SolanaUser() {
}
@@ -15,14 +25,14 @@ public class SolanaUser {
public SolanaUser(long loginId,
String login,
long bchId,
String pubkey0,
String pubkey1,
String loginKey,
String deviceKey,
Integer bchLimit) {
this.loginId = loginId;
this.login = login;
this.bchId = bchId;
this.pubkey0 = pubkey0;
this.pubkey1 = pubkey1;
this.loginKey = loginKey;
this.deviceKey = deviceKey;
this.bchLimit = bchLimit;
}
@@ -50,20 +60,22 @@ public class SolanaUser {
this.bchId = bchId;
}
public String getPubkey0() {
return pubkey0;
/** Публичный ключ логина (основной ключ пользователя). */
public String getLoginKey() {
return loginKey;
}
public void setPubkey0(String pubkey0) {
this.pubkey0 = pubkey0;
public void setLoginKey(String loginKey) {
this.loginKey = loginKey;
}
public String getPubkey1() {
return pubkey1;
/** Публичный ключ устройства (device key). */
public String getDeviceKey() {
return deviceKey;
}
public void setPubkey1(String pubkey1) {
this.pubkey1 = pubkey1;
public void setDeviceKey(String deviceKey) {
this.deviceKey = deviceKey;
}
public Integer getBchLimit() {