diff --git a/.idea/dataSources.xml b/.idea/dataSources.xml new file mode 100644 index 0000000..409263e --- /dev/null +++ b/.idea/dataSources.xml @@ -0,0 +1,30 @@ + + + + + postgresql + true + org.postgresql.Driver + jdbc:postgresql://localhost:5432/postgres + + + + + + + $ProjectFileDir$ + + + postgresql + true + org.postgresql.Driver + jdbc:postgresql://localhost:5432/postgres + + + + + + $ProjectFileDir$ + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..601f39c --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/sqldialects.xml b/.idea/sqldialects.xml new file mode 100644 index 0000000..a7c53a0 --- /dev/null +++ b/.idea/sqldialects.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/Client/pom.xml b/Client/pom.xml index e7ad365..d00cd2a 100644 --- a/Client/pom.xml +++ b/Client/pom.xml @@ -17,4 +17,17 @@ UTF-8 + + + org.openjfx + javafx-controls + 23.0.2 + + + org.openjfx + javafx-base + 23.0.2 + + + \ No newline at end of file diff --git a/Client/src/main/java/ru/project/ChatClientGUI.java b/Client/src/main/java/ru/project/ChatClientGUI.java new file mode 100644 index 0000000..37d5a53 --- /dev/null +++ b/Client/src/main/java/ru/project/ChatClientGUI.java @@ -0,0 +1,115 @@ +package ru.project; +import javafx.application.Application; +import javafx.application.Platform; +import javafx.scene.Scene; +import javafx.scene.control.Button; +import javafx.scene.control.TextArea; +import javafx.scene.control.TextField; +import javafx.scene.layout.VBox; +import javafx.stage.Stage; +import javafx.geometry.Insets; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.net.Socket; + +public class ChatClientGUI extends Application { + private TextArea chatArea; + private TextField inputField; + private Button sendButton; + private Socket socket; + private DataInputStream in; + private DataOutputStream out; + private boolean isBanned = false; + + @Override + public void start(Stage primaryStage) { + chatArea = new TextArea(); + chatArea.setEditable(false); + chatArea.setWrapText(true); + + inputField = new TextField(); + inputField.setPromptText("Введите сообщение..."); + + Button sendButton = new Button("Отправить"); + sendButton.setOnAction(e -> sendMessage()); + + VBox root = new VBox(10,chatArea, inputField, sendButton); + root.setPadding(new Insets(10, 10, 10, 10)); + Scene scene = new Scene(root, 400, 300); + + primaryStage.setTitle("Чат-клиент"); + primaryStage.setScene(scene); + primaryStage.show(); + + connectToServer(); + } + + public static void main(String[] args) { + launch(args); + } + + private void sendMessage() { + String message = inputField.getText().trim(); + if (!message.isEmpty() && !isBanned) { + try { + out.writeUTF(message); // Отправляем на сервер + inputField.clear(); // Очищаем поле + } catch (IOException e) { + chatArea.appendText("Ошибка отправки сообщения\n"); + } + } else if (isBanned) { + chatArea.appendText("Вы не можете писать сообщения, так как заблокированы.\n"); + } + } + private void connectToServer() { + try { + socket = new Socket("localhost", 8189); + in = new DataInputStream(socket.getInputStream()); + out = new DataOutputStream(socket.getOutputStream()); + chatArea.appendText("Подключено к серверу\n"); + + new Thread(() -> { + try { + while (true) { + String message = in.readUTF(); + Platform.runLater(() -> processServerMessage(message)); + } + } catch (Exception e) { + Platform.runLater(() -> chatArea.appendText(e.getMessage() + "\n")); + } + }).start(); + } catch (Exception e) { + chatArea.appendText(e.getMessage() + "\n"); + } + } + private void processServerMessage(String message) { + if (message.startsWith("/")) { + if (message.equalsIgnoreCase("/exitok")) { + disconnect(); + } else if (message.startsWith("/authOK ")) { + chatArea.appendText("Авторизация успешна! Ваш ник: " + message.split(" ")[1] + "\n"); + } else if (message.startsWith("/regOK ")) { + chatArea.appendText("Регистрация успешна! Ваш ник: " + message.split(" ")[1] + "\n"); + } else if (message.equalsIgnoreCase("/banok")) { + isBanned = true; + chatArea.appendText("Вы были заблокированы администратором.\n"); + } else if (message.equalsIgnoreCase("/unbanok")) { + isBanned = false; + chatArea.appendText("Вы теперь разбанены и можете писать сообщения.\n"); + } + } else { + chatArea.appendText(message + "\n"); // Обычное сообщение + } + } + private void disconnect() { + try { + if (socket != null) socket.close(); + if (in != null) in.close(); + if (out != null) out.close(); + chatArea.appendText("Отключено от сервера\n"); + } catch (IOException e) { + e.printStackTrace(); + } + } +} diff --git a/Client/src/main/java/ru/project/Client.java b/Client/src/main/java/ru/project/Client.java new file mode 100644 index 0000000..5d7f515 --- /dev/null +++ b/Client/src/main/java/ru/project/Client.java @@ -0,0 +1,96 @@ +package ru.project; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.net.Socket; +import java.net.SocketException; +import java.util.Scanner; + +public class Client { + private Socket socket; + private DataOutputStream out; + private DataInputStream in; + private Scanner scanner; + private boolean isBanned = false; + + public Client() throws IOException { + scanner = new Scanner(System.in); + socket = new Socket("localhost", 8189); + out = new DataOutputStream(socket.getOutputStream()); + in = new DataInputStream(socket.getInputStream()); + + new Thread(() -> { + try { + while (!socket.isClosed()) { + String message = in.readUTF(); + if (message.startsWith("/")) { + if (message.equalsIgnoreCase("/exitok")) { + break; + } + if (message.startsWith("/authOK ")) { + System.out.println("Авторизация прошла успешно! Имя пользователя: " + + message.split(" ")[1]); + } + if (message.startsWith("/regOK ")) { + System.out.println("Регистрация прошла успешно! Имя пользователя: " + + message.split(" ")[1]); + } + if (message.equalsIgnoreCase("/banok")) { + isBanned = true; + System.out.println("Вы были заблокированы администратором."); + } + if (message.equalsIgnoreCase("/unbanok")) { + isBanned = false; + System.out.println("Теперь вы не заблокированы. Вам доступна отправка сообщений в чат."); + } + } else { + System.out.println(message); + } + } + } catch (IOException e) { + e.printStackTrace(); + } finally { + disconnect(); + } + }).start(); + + while (!socket.isClosed()) { + String message = scanner.nextLine(); + try { + if (!isBanned) { + out.writeUTF(message); + } else System.out.println("Вы не можете писать сообщения."); + } catch (SocketException e) { + System.out.println("Вы были отключены от сервера."); + } + if (message.equalsIgnoreCase("/exit")) { + break; + } + } + } + + public void disconnect() { + try { + if (in != null) { + in.close(); + } + } catch (IOException e) { + e.printStackTrace(); + } + try { + if (out != null) { + out.close(); + } + } catch (IOException e) { + e.printStackTrace(); + } + try { + if (socket != null) { + socket.close(); + } + } catch (IOException e) { + e.printStackTrace(); + } + } +} diff --git a/Client/src/main/java/ru/project/ClientApplication.java b/Client/src/main/java/ru/project/ClientApplication.java new file mode 100644 index 0000000..e4252de --- /dev/null +++ b/Client/src/main/java/ru/project/ClientApplication.java @@ -0,0 +1,14 @@ +package ru.project; + +import java.io.IOException; + + +public class ClientApplication { + public static void main(String[] args) { + try { + new Client(); + } catch (IOException e) { + e.printStackTrace(); + } + } +} diff --git a/Server/src/main/java/ru/project/Authenticator.java b/Server/src/main/java/ru/project/Authenticator.java new file mode 100644 index 0000000..df49723 --- /dev/null +++ b/Server/src/main/java/ru/project/Authenticator.java @@ -0,0 +1,7 @@ +package ru.project; + +public interface Authenticator { + void initialize(); + boolean authenticate(ClientHandler clientHandler, String login, String password); + boolean registration(ClientHandler clientHandler, String username, String login, String password); +} diff --git a/Server/src/main/java/ru/project/ClientHandler.java b/Server/src/main/java/ru/project/ClientHandler.java index 6bdca6f..c15ac90 100644 --- a/Server/src/main/java/ru/project/ClientHandler.java +++ b/Server/src/main/java/ru/project/ClientHandler.java @@ -4,44 +4,143 @@ import java.io.DataOutputStream; import java.io.IOException; import java.net.Socket; +import java.text.SimpleDateFormat; +import java.util.Date; public class ClientHandler { - private Socket socket; - private Server server; - private DataInputStream in; - private DataOutputStream out; - + private final Socket socket; + private final Server server; + private final DataInputStream in; + private final DataOutputStream out; private String username; - private static int userCount = 0; - + private Role role; + private final long lastActiveTime; + private String currentRoom = null; + boolean banFlag; public ClientHandler(Socket socket, Server server) throws IOException { this.socket = socket; this.server = server; this.in = new DataInputStream(socket.getInputStream()); this.out = new DataOutputStream(socket.getOutputStream()); - - userCount++; - username = "user" + userCount; + this.lastActiveTime = System.currentTimeMillis(); new Thread(() -> { try { System.out.println("Клиент подключился " + socket.getPort()); - + //Цикл логина while (true) { + sendMsg(""" + Для начала работы надо пройти аутентификацию или регистрацию + Формат команды для аутентификации: /log + Формат команды для регистрации: /reg + Для выхода используйте комманду /exit"""); String message = in.readUTF(); + if (message.startsWith("/")) { + if (message.equals("/exit")) { + sendMsg("/exitOK"); + break; + } + if (message.equalsIgnoreCase("/log")) { + String[] loginData = new String[2]; + sendMsg("Введите логин: "); + loginData[0] = in.readUTF(); + sendMsg("Введите пароль: "); + loginData[1] = in.readUTF(); + if (server.getAuthenticator() + .authenticate(this, loginData[0], loginData[1])) { + break; + } else { + sendMsg("Ошибка авторизации!"); + continue; + } + } + if (message.equalsIgnoreCase("/reg")) { + String[] regData = new String[3]; + sendMsg(""" + Введите никнейм. + Никнейм не должен содержать более 20 символов, и не менее 5 символов + Можно использовать цифры, латинские буквы и знаки "_" и "-\""""); + regData[0] = in.readUTF(); + sendMsg(""" + Введите логин. + Логин не должен содержать более 20 символов, и не менее 5 символов + Можно использовать цифры, латинские буквы и знаки "_" и "-\""""); + regData[1] = in.readUTF(); + sendMsg("Введите пароль.\n" + + "Пароль должен содержать от 5 до 20 символов и обязательно включать в себя любой специальный символ."); + regData[2] = in.readUTF(); + if (server.getAuthenticator() + .registration(this, regData[0], regData[1], regData[2])) { + break; + } else { + sendMsg("Ошибка регистрации!"); + } + } + } + else { + sendMsg("Неверная команда"); + } + } + //Цикл работы + while (true) { + String message = in.readUTF(); + this.banFlag = false; if (message.startsWith("/")) { if (message.equalsIgnoreCase("/exit")) { sendMsg("/exitok"); break; + } else if (message.startsWith("/w ")) { + String[] tokens = message.split(" ", 3); + if (tokens.length == 3) { + String recipient = tokens[1]; + String privateMessage = tokens[2]; + server.sendPrivateMessage(this, recipient, privateMessage); + } else { + sendMsg("Неверный формат комманды. Используйте /w <никнейм> <сообщение>"); + } + } else if (message.startsWith("/kick ")) { + String[] tokens = message.split(" ", 2); + if (tokens.length != 2) { + sendMsg("Неверный формат команды /kick"); + continue; + } + server.kickUser(tokens[1], this); + } else if (message.startsWith("/ban ")) { + String[] tokens = message.split(" ", 2); + if (tokens.length != 2) { + sendMsg("Неверный формат команды /ban"); + continue; + } + server.banUser(tokens[1], this); + } else if (message.startsWith("/unban ")) { + String[] tokens = message.split(" ", 2); + if (tokens.length != 2) { + sendMsg("Неверный формат комманды /unban"); + continue; + } + server.unbanUser(tokens[1], this); + } else if (message.equalsIgnoreCase("/online")) { + sendMsg(server.getOnlineUsers()); + } else if (message.startsWith("/createroom ")) { + String roomName = message.substring(12).trim(); + server.createRoom(roomName, this); + } else if (message.startsWith("/room ")) { + String roomName = message.substring(6).trim(); + server.joinRoom(roomName, this); + } else if (message.equals("/exitroom")) { + server.exitRoom(this); } + } else if (currentRoom != null) { + server.sendToRoom(currentRoom, "[" + getCurrentTime() + "] " + username + ": " + message); } else { - server.broadcastMessage(username + " : " + message); + String messageWithTime = "[" + getCurrentTime() + "]" + username + " : " + message; + server.broadcastMessage(messageWithTime); } } } catch (IOException e) { - e.printStackTrace(); + System.out.println("Сокет закрыт, клиент на порту " + socket.getPort() + " отключен."); } finally { disconnect(); } @@ -84,4 +183,31 @@ public void disconnect() { public String getUsername() { return username; } + public void setUsername(String username) { + this.username = username; + } + public void setRole(Role role) { + this.role = role; + } + public Role getRole() { + return role; + } + private String getCurrentTime() { + return new SimpleDateFormat("HH:mm:ss").format(new Date()); + } + public long getLastActiveTime() { + return lastActiveTime; + } + public void setCurrentRoom(String roomName) { + this.currentRoom = roomName; + } + public String getCurrentRoom() { + return currentRoom; + } + public boolean isBanned() { + return banFlag; + } + public void changeBanFlag() { + banFlag = !banFlag; + } } diff --git a/Server/src/main/java/ru/project/Role.java b/Server/src/main/java/ru/project/Role.java new file mode 100644 index 0000000..b3efc8a --- /dev/null +++ b/Server/src/main/java/ru/project/Role.java @@ -0,0 +1,6 @@ +package ru.project; + +public enum Role { + USER, + ADMIN; +} diff --git a/Server/src/main/java/ru/project/Server.java b/Server/src/main/java/ru/project/Server.java index 6fe59bc..29c4a6e 100644 --- a/Server/src/main/java/ru/project/Server.java +++ b/Server/src/main/java/ru/project/Server.java @@ -1,5 +1,9 @@ package ru.project; +import ru.project.checktime.CheckTime; +import ru.project.database.DatabaseManager; +import ru.project.rooms.Rooms; + import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; @@ -9,18 +13,25 @@ public class Server { private int port; private List clients; + private Authenticator authenticator; + private final DatabaseManager databaseManager; + private final Rooms rooms = new Rooms(); - public Server(int port) { + public Server(int port, DatabaseManager databaseManager) { this.port = port; + this.databaseManager = databaseManager; clients = new CopyOnWriteArrayList<>(); - } + authenticator = new inMemoryAuthenticator(this); + new CheckTime(this).start(); + } public void start(){ try (ServerSocket serverSocket = new ServerSocket(port)) { System.out.println("Сервер запущен на порту: " + port); + authenticator.initialize(); while (true) { Socket socket = serverSocket.accept(); - subscribe(new ClientHandler(socket, this)); + new ClientHandler(socket, this); } } catch (IOException e) { @@ -30,11 +41,14 @@ public void start(){ public void subscribe(ClientHandler clientHandler){ clients.add(clientHandler); + broadcastMessage("В чат зашел: " + clientHandler.getUsername()); + System.out.println("[SERVER] Пользователь " + clientHandler.getUsername() + " подключился."); } public void unsubscribe(ClientHandler clientHandler){ clients.remove(clientHandler); broadcastMessage("Из чата вышел: "+ clientHandler.getUsername()); + System.out.println("[SERVER] Клиент " + clientHandler.getUsername() + " отключён."); } public void broadcastMessage(String message){ @@ -42,4 +56,110 @@ public void broadcastMessage(String message){ c.sendMsg(message); } } + public void sendPrivateMessage(ClientHandler sender, String recipient, String message) { + for (ClientHandler client : clients) { + if (client.getUsername().equals(recipient)) { + client.sendMsg(sender.getUsername() + ": " + message); + sender.sendMsg(recipient + ": " + message); + return; + } + } + sender.sendMsg("Ошибка: пользователь с ником " + recipient + " не найден."); + } + public boolean isUserLoggedIn(String username){ + for (ClientHandler client : clients) { + if (client.getUsername().equals(username)) { + return true; + } + } + return false; + } + public Authenticator getAuthenticator() { + return authenticator; + } + public void kickUser(String usernameToKick, ClientHandler adminHandler) { + if (adminHandler.getRole().equals(Role.ADMIN)) { + for (ClientHandler client : clients) { + if (client.getUsername().equals(usernameToKick)) { + client.sendMsg("/exitok"); + broadcastMessage("Пользователь " + usernameToKick + " был отключён администратором"); + return; + } + } + adminHandler.sendMsg("Ошибка. Пользователь с ником " + usernameToKick + " не найден"); + } else { + adminHandler.sendMsg("Недостаточно прав"); + } + } + public void banUser(String usernameToBan, ClientHandler adminHandler) { + if (adminHandler.getRole().equals(Role.ADMIN)) { + for (ClientHandler client : clients) { + if (client.getUsername().equals(usernameToBan)) { + if (client.isBanned()) { + adminHandler.sendMsg("Ошибка. Пользователь с ником " + usernameToBan + " уже забанен!"); + } else { + client.sendMsg("/banok"); + broadcastMessage("Пользователь " + usernameToBan + " был забанен администратором"); + client.changeBanFlag(); + } + return; + } + } + adminHandler.sendMsg("Ошибка. Пользователь с ником " + usernameToBan + " не найден"); + } else { + adminHandler.sendMsg("Недостаточно прав"); + } + } + public void unbanUser(String usernameToUnban, ClientHandler adminHandler) { + if (adminHandler.getRole().equals(Role.ADMIN)) { + for (ClientHandler client : clients) { + if (client.getUsername().equals(usernameToUnban)) { + if (!client.isBanned()) { + adminHandler.sendMsg("Ошибка. Пользователь с ником " + usernameToUnban + " не находится в бане"); + } else { + client.sendMsg("/unbanok"); + broadcastMessage("Пользователь " + usernameToUnban + " был разбанен администратором"); + client.changeBanFlag(); + } + return; + } + } + adminHandler.sendMsg("Ошибка. Пользователь с ником " + usernameToUnban + " не найден"); + } else { + adminHandler.sendMsg("Недостаточно прав"); + } + } + public DatabaseManager getDatabaseManager() { + return databaseManager; + } + + public String getOnlineUsers() { + StringBuilder sb = new StringBuilder("Сейчас онлайн:\n"); + for (ClientHandler client : clients) { + sb.append(client.getUsername()).append("\n"); + } + return sb.toString(); + } + public List getClients() { + return this.clients; + } + public void createRoom(String roomName, ClientHandler owner) { + rooms.createRoom(roomName, owner); + } + + public void joinRoom(String roomName, ClientHandler client) { + rooms.joinRoom(roomName, client); + } + + public void exitRoom(ClientHandler client) { + rooms.exitRoom(client); + } + + public void sendToRoom(String roomName, String message) { + rooms.sendToRoom(roomName, message); + } + + public boolean isUserInRoom(ClientHandler client) { + return rooms.isUserInRoom(client); + } } diff --git a/Server/src/main/java/ru/project/ServerApplication.java b/Server/src/main/java/ru/project/ServerApplication.java index 2dbdb42..f44d85c 100644 --- a/Server/src/main/java/ru/project/ServerApplication.java +++ b/Server/src/main/java/ru/project/ServerApplication.java @@ -1,7 +1,9 @@ package ru.project; +import ru.project.database.DatabaseManager; + public class ServerApplication { public static void main(String[] args) { - new Server(8189).start(); + new Server(8189, new DatabaseManager()).start(); } } diff --git a/Server/src/main/java/ru/project/checktime/CheckTime.java b/Server/src/main/java/ru/project/checktime/CheckTime.java new file mode 100644 index 0000000..60aad48 --- /dev/null +++ b/Server/src/main/java/ru/project/checktime/CheckTime.java @@ -0,0 +1,32 @@ +package ru.project.checktime; + +import ru.project.*; + +public class CheckTime extends Thread { + private static final long TIMEOUT = 20 * 60 * 1000; + private final Server server; + + public CheckTime(Server server) { + this.server = server; + } + @Override + public void run() { + while (true) { + try { + Thread.sleep(60*1000); + long currentTime = System.currentTimeMillis(); + + for (ClientHandler client : server.getClients()) { + if (currentTime - client.getLastActiveTime() > TIMEOUT) { + System.out.println("Отключение неактивного клиента: " + client.getUsername()); + client.sendMsg("/exitok"); + server.unsubscribe(client); + client.disconnect(); + } + } + } catch (InterruptedException e) { + server.broadcastMessage("Ошибка CheckTime"); + } + } + } +} diff --git a/Server/src/main/java/ru/project/database/DatabaseManager.java b/Server/src/main/java/ru/project/database/DatabaseManager.java new file mode 100644 index 0000000..cdc8cac --- /dev/null +++ b/Server/src/main/java/ru/project/database/DatabaseManager.java @@ -0,0 +1,120 @@ +package ru.project.database; + +import ru.project.Role; +import java.sql.*; + +public class DatabaseManager { + private static final String DB_URL = "jdbc:postgresql://localhost:5432/postgres?currentSchema=public&user=admin&password=admin"; + + + public DatabaseManager() { + try (Connection conn = DriverManager.getConnection(DB_URL); + Statement stmt = conn.createStatement()) { + String createTableSQL = """ + CREATE TABLE IF NOT EXISTS users ( + id SERIAL PRIMARY KEY, + username TEXT NOT NULL UNIQUE, + login TEXT NOT NULL UNIQUE, + password TEXT NOT NULL, + role TEXT NOT NULL + ); + """; + String addAdmin = """ + INSERT INTO users (username, login, password, role) + SELECT 'admin', 'admin', 'admin', 'ADMIN' + WHERE NOT EXISTS (SELECT 1 FROM users WHERE username = 'admin'); + """; + stmt.execute(createTableSQL); + stmt.executeUpdate(addAdmin); + } catch (SQLException e) { + e.printStackTrace(); + } + } + public String authenticate(String login, String password) { + String query = "SELECT username FROM users WHERE login = ? AND password = ?"; + try (Connection conn = DriverManager.getConnection(DB_URL); + PreparedStatement pstmt = conn.prepareStatement(query)) { + pstmt.setString(1, login); + pstmt.setString(2, password); + ResultSet rs = pstmt.executeQuery(); + if (rs.next()) { + return rs.getString("username"); + } + } catch (SQLException e) { + e.printStackTrace(); + } + return null; + } + public boolean registerUser(String username, String login, String password, Role role) { + String query = "INSERT INTO users (username, login, password, role) VALUES (?, ?, ?, ?)"; + try (Connection conn = DriverManager.getConnection(DB_URL); + PreparedStatement pstmt = conn.prepareStatement(query)) { + pstmt.setString(1, username); + pstmt.setString(2, login); + pstmt.setString(3, password); + pstmt.setString(4, String.valueOf(role)); + pstmt.executeUpdate(); + return true; + } catch (SQLException e) { + if (e.getMessage().contains("UNIQUE")) { + System.out.println("Логин или имя пользователя уже существуют."); + } else { + e.printStackTrace(); + } + return false; + } + } + public String getUserRole(String username) { + String query = "SELECT role FROM users WHERE username = ?"; + try (Connection conn = DriverManager.getConnection(DB_URL); + PreparedStatement pstmt = conn.prepareStatement(query)) { + pstmt.setString(1, username); + ResultSet rs = pstmt.executeQuery(); + if (rs.next()) { + return rs.getString("role"); + } + } catch (SQLException e) { + e.printStackTrace(); + } + return null; + } + public boolean isUsernameAlreadyExist(String username) { + String query = "SELECT 1 FROM users WHERE username = ?"; + try (Connection conn = DriverManager.getConnection(DB_URL); + PreparedStatement pstmt = conn.prepareStatement(query)) { + pstmt.setString(1, username); + ResultSet rs = pstmt.executeQuery(); + return rs.next(); + } catch (SQLException e) { + e.printStackTrace(); + return false; + } + } + public boolean isLoginAlreadyExist(String login) { + String query = "SELECT 1 FROM users WHERE login = ?"; + try (Connection conn = DriverManager.getConnection(DB_URL); + PreparedStatement pstmt = conn.prepareStatement(query)) { + pstmt.setString(1, login); + ResultSet rs = pstmt.executeQuery(); + return rs.next(); + } catch (SQLException e) { + e.printStackTrace(); + return false; + } + } + public String getUsernameByLoginAndPassword(String login, String password) { + String query = "SELECT username FROM users WHERE username = ? AND password = ?"; + try (Connection conn = DriverManager.getConnection(DB_URL); + PreparedStatement pstmt = conn.prepareStatement(query)) { + pstmt.setString(1, login); + pstmt.setString(2, password); + ResultSet rs = pstmt.executeQuery(); + if (rs.next()) { + return rs.getString("username"); + } + } catch (SQLException e) { + e.printStackTrace(); + } + return null; + } +} diff --git a/Server/src/main/java/ru/project/inMemoryAuthenticator.java b/Server/src/main/java/ru/project/inMemoryAuthenticator.java new file mode 100644 index 0000000..bd3a72e --- /dev/null +++ b/Server/src/main/java/ru/project/inMemoryAuthenticator.java @@ -0,0 +1,86 @@ +package ru.project; + +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; + +public class inMemoryAuthenticator implements Authenticator { + public static class User { + private final String username; + private final String login; + private final String password; + + public User(String username, String login, String password) { + this.username = username; + this.login = login; + this.password = password; + } + } + private final List users; + private final Server server; + public inMemoryAuthenticator(Server server) { + users = new CopyOnWriteArrayList<>(); + this.server = server; + users.add(new User("admin", "admin", "admin")); + } + @Override + public void initialize() { + System.out.println("Инициализация"); + } + @Override + public boolean authenticate(ClientHandler clientHandler, String login, String password) { + String username = server.getDatabaseManager().getUsernameByLoginAndPassword(login, password); + if (username == null) { + clientHandler.sendMsg("Неверный логин и/или пароль"); + return false; + } + if (server.isUserLoggedIn(username)) { + clientHandler.sendMsg("Данный пользователь уже в сети"); + return false; + } + clientHandler.setUsername(server.getDatabaseManager().authenticate(login, password)); + if (server.getDatabaseManager().getUserRole(username).equals("ADMIN")) { + clientHandler.setRole(Role.ADMIN); + } else { + clientHandler.setRole(Role.USER); + } + server.subscribe(clientHandler); + clientHandler.sendMsg("/authOK " + username); + return true; + } + @Override + public boolean registration(ClientHandler clientHandler, String username, String login, String password) { + String regex = "^[a-zA-Z0-9_-]{5,20}$"; + String regexForPassword = "^(?=.*[!@#$%^&*(),.?\":{}|<>])[a-zA-Z0-9!@#$%^&*(),.?\":{}|<>]{5,20}$"; + if(!username.matches(regex)) { + clientHandler.sendMsg("Убедитесь, что имя пользователя соответствует требованиям"); + return false; + } + if(!login.matches(regex)) { + clientHandler.sendMsg("Убедитесь, что логин соответствует требованиям"); + return false; + } + if (!password.matches(regexForPassword)) { + clientHandler.sendMsg("Убедитесь, что пароль соответствует требованиям"); + return false; + } + if (server.getDatabaseManager().isLoginAlreadyExist(login)) { + clientHandler.sendMsg("Такой логин уже используется"); + return false; + } + if (server.getDatabaseManager().isUsernameAlreadyExist(username)) { + clientHandler.sendMsg("Такое имя пользователя уже используется"); + return false; + } + server.getDatabaseManager().registerUser(username, login, password, Role.USER); + users.add(new User(username, login, password)); + clientHandler.setUsername(username); + if (clientHandler.getUsername().equals("admin")) { + clientHandler.setRole(Role.ADMIN); + } else { + clientHandler.setRole(Role.USER); + } + server.subscribe(clientHandler); + clientHandler.sendMsg("/regOK " + username); + return true; + } +} diff --git a/Server/src/main/java/ru/project/rooms/Rooms.java b/Server/src/main/java/ru/project/rooms/Rooms.java new file mode 100644 index 0000000..bcd8a5b --- /dev/null +++ b/Server/src/main/java/ru/project/rooms/Rooms.java @@ -0,0 +1,62 @@ +package ru.project.rooms; + +import ru.project.ClientHandler; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CopyOnWriteArrayList; + +public class Rooms { + private final Map> rooms = new ConcurrentHashMap<>(); + + public void createRoom(String roomName, ClientHandler owner) { + if (rooms.containsKey(roomName)) { + owner.sendMsg("Комната с таким именем уже существует."); + return; + } + if (owner.getCurrentRoom() != null) { + owner.sendMsg("Вы уже создали комнату: " + owner.getCurrentRoom()); + return; + } + rooms.put(roomName, new CopyOnWriteArrayList<>(List.of(owner))); + owner.setCurrentRoom(roomName); + owner.sendMsg("Вы создали комнату: " + roomName); + } + + public void joinRoom(String roomName, ClientHandler client) { + if (!rooms.containsKey(roomName)) { + client.sendMsg("Комната " + roomName + " не существует."); + return; + } + if (client.getCurrentRoom() != null) { + client.sendMsg("Вы уже находитесь в комнате: " + client.getCurrentRoom()); + return; + } + rooms.get(roomName).add(client); + client.setCurrentRoom(roomName); + sendToRoom(roomName, client.getUsername() + " присоединился к комнате."); + } + + public void exitRoom(ClientHandler client) { + String roomName = client.getCurrentRoom(); + if (roomName == null) { + client.sendMsg("Вы не находитесь в комнате."); + return; + } + rooms.get(roomName).remove(client); + client.setCurrentRoom(null); + sendToRoom(roomName, client.getUsername() + " покинул комнату."); + client.sendMsg("Вы вышли из комнаты."); + } + public void sendToRoom(String roomName, String message) { + if (!rooms.containsKey(roomName)) return; + for (ClientHandler client : rooms.get(roomName)) { + client.sendMsg(message); + } + } + + public boolean isUserInRoom(ClientHandler client) { + return client.getCurrentRoom() != null; + } +} diff --git a/pom.xml b/pom.xml index 7271f2e..4e0fbfe 100644 --- a/pom.xml +++ b/pom.xml @@ -12,11 +12,19 @@ Server Client - + + + + org.postgresql + postgresql + 42.7.4 + + 23 23 UTF-8 + \ No newline at end of file