Serwer client aplikacja - problem z podwójnym otrzymywaniem wiadomości od serwera

0

Mam problem z "systemem logowania" do mojej aplikacji.

Tabela użytkowników (0 i 1 określa czy użytkownik jest adminem czy nie): obraz_2021-04-16_132830.png

Zmienne, które pobierają dane:

 String userName = ftUser.getText();
 String password = String.valueOf(ftPassword.getPassword());

Funkcja, do której wysyłam te dane:

 public int login(String login, String password) throws IOException, ClassNotFoundException {

    LoginData data = new LoginData();

    data.setLogin(login);
    data.setPassword(password);
    data.setType("login");

    outer = new ObjectOutputStream(socket.getOutputStream());
    outer.writeObject(data);

    writer = new ObjectInputStream(socket.getInputStream());
    LoginData received = (LoginData) writer.readObject();

    System.out.println("Serwer says: " + received.getType());

    if(received.getType().equals("ok admin"))
        return 2;
    else if (received.getType().equals("ok user"))
        return 1;
    else
        return 0;

}

Funkcja, gdzie przetwarzam te dane:

while(true) {

            writer = new ObjectInputStream(client.getInputStream());
            LoginData received = (LoginData) writer.readObject();

                if(received.getType().equals("login")) {
                    setLogin(received.getLogin());
                    setPassword(received.getPassword());

                    //System.out.println(getLogin()+getPassword());

                    loginHandler();
}

private void loginHandler() throws IOException, SQLException {
    ObjectOutput outer = new ObjectOutputStream(client.getOutputStream());
    LoginData ld = new LoginData();
    stmt = db.connect().createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,
            ResultSet.CONCUR_READ_ONLY);
    stmt.executeUpdate("USE biblioteka");
    String st = ("SELECT * FROM users WHERE login='"+getLogin()+"' AND haslo='"+getPassword()+"'");

    rs = stmt.executeQuery(st);

    if (rs.next()) {
            System.out.println("Witamy w bazie!");
            System.out.println("Udane logowanie: " + getLogin());
            String admin = rs.getString("admin");
            if (admin.equals("1")) {
                ld.setType("ok admin");
                outer.writeObject(ld);

            } else if (admin.equals("0")) {
                ld.setType("ok user");
                outer.writeObject(ld);
           


            }
                db.closeDatabase();

    } else {
        ld.setType("error login");
        outer.writeObject(ld);
        System.out.println(ld);
        System.err.println("Error Login " + getLogin());
        db.closeDatabase();
    }

Mój problem polega na tym, że jeśli loguje się do konta zwykłego użytkownika wszystko działa poprawnie. Serwer odpowiada tylko raz i kieruje mnie do odpowiedniego okienka. Jeśli jednak logowanie nie powiedzie się lub następuje logowanie do konta admina to server odpowiada mi podwójnie. Wygląda to mniej więcej tak:

Serwer says: ok user

Serwer says: ok admin
Serwer says: ok admin

Czy ktoś mógłby mi podpowiedzieć na czym polega mój błąd i ewentualnie naprowadzić jak go rozwiązać?

1

Wygląda na to, że to nie serwer odpowiada podwójnie a Ty podwójnie wywołujesz metodę login, ale z tego co tutaj wkleiłeś nie można wywnioskować dlaczego (pomijam fakt że nie zamykasz streamów / połączeń co może mieć jakieś nieprzewidziane skutki).

4

Ale ładne.

sygnatury z dopiskiem:
throws IOException, ClassNotFoundException
bo jak się loguje ktoś kto nie ma klasy to leci ClassNotFoundException - logiczne

obiekty przekazywane serializacją (nie tragedia, ale daje ciekawe możliwości ataków)

a na koniec wisenka na torcie w postaci SQLInjection

0

To jedyne miejsce, gdzie wywołuję klase login. Czy istnieje możliwość, że tutaj robię coś źle?

 bLogin.addActionListener((event) ->{

            String userName = ftUser.getText();
            String password = String.valueOf(ftPassword.getPassword());

            if (userName.equals("") || password.equals("")) {
                JOptionPane.showMessageDialog(null, "Uzupełnij pola!");
            } else {

                try {
                    if (cHandler.login(userName, password) == 1) {

                        new UserInterface(cHandler.getSocket());
                        ftUser.setText("");
                        ftPassword.setText("");


                    } else if (cHandler.login(userName, password) == 2) {
                        new AdminInterface(cHandler.getSocket());
                        ftUser.setText("");
                        ftPassword.setText("");

                    } else {
                        JOptionPane.showMessageDialog(null, "Złe dane!");
                    }

                } catch (IOException | ClassNotFoundException | SQLException e) {

                    e.printStackTrace();

                }

            }

        });
0

Tak, robisz źle. Logujesz się dwa razy - to widać od razu.
Ale nie wiem jak Ci pomóc :-( - bo ten kod jest tak straszny po całości, że trudno powiedzieć gdzie zacząć.
Masz jakiś debugger? Przeklikaj sobie krok po kroku tego addActionListenera.

1
Mariusz Golonko napisał(a):
                if (cHandler.login(userName, password) == 1) {

                    new UserInterface(cHandler.getSocket());
                    ftUser.setText("");
                    ftPassword.setText("");


                } else if (cHandler.login(userName, password) == 2) {
                    new AdminInterface(cHandler.getSocket());
                    ftUser.setText("");
                    ftPassword.setText("");

                } else {
                    JOptionPane.showMessageDialog(null, "Złe dane!");
                }

Tu wołasz login 2 razy. Wystarczy raz i zapisać wynik

0

A już całkiem łopatologicznie: :)

loginResult = cHandler.login(userName, password);
if (loginResult == 1) {
    ...
} else if (loginResult == 2) {
    ...
} else {
    ...
2
    writer = new ObjectInputStream(socket.getInputStream());
    LoginData received = (LoginData) writer.readObject();

A ja naiwny zawsze myśle że takie eksploity to tylko na CTFach są xD Zastanów się co się stanie jak zrobisz readObject a po drugiej stronie czeka obiekt który formatuje ci dysk twardy ;)
Popatrz sobie na https://github.com/frohoff/ysoserial który służy do generowania eksploitów na coś takiego.

Przykłady jak można coś takiego eksploitować: https://github.com/p4-team/ctf/tree/master/2018-09-15-trendmicro/misc_deserializer

1 użytkowników online, w tym zalogowanych: 0, gości: 1