Authentification MySQL/Swing/Java

Fermé
Hurobaki Messages postés 53 Date d'inscription dimanche 23 mars 2014 Statut Membre Dernière intervention 10 mars 2017 - 21 févr. 2016 à 10:48
KX Messages postés 16753 Date d'inscription samedi 31 mai 2008 Statut Modérateur Dernière intervention 25 novembre 2024 - 21 févr. 2016 à 12:44
Bonjour à tous !

Voilà j'ai décidé de commencer à faire du développement IHM sous Java. J'ai donc créé des interfaces plutôt simple puis un peu plus compliqué avec des events et tout.

Ensuite j'ai voulu me connecter à une DB MySQL, ce que j'ai réussi à faire.

Mon problème aujourd'hui est l'authentification, j'arrive à vérifier dans ma DB si le login correspond au mot de passe. Ce que je souhaite c'est grâce à cela, créer un utilisateur unique. Hop design pattern Singleton sur une classe User !

Je ne pensais pas avoir de problème ... Sauf que je n'arrive pas à récupérer mon utilisateur dans la classe main qui me servira pour communiquer avec l'utilisateur.

Je vous montre le code :

public class User {
	
	private static User instance;
	String login;
	String mdp;
	String droit;
	
	
	private User(String login, String mdp, String droit) 
	{
		this.login = login;
		this.mdp = mdp;
		this.droit = droit;
	}
	
	public static synchronized User getInstance(String login, String mdp, String droit)
	{
		if(instance == null)
		{
			instance = new User(login, mdp, droit);
		}
		return instance;
	}

	@Override
	public String toString() {
		return "User [login=" + login + ", mdp=" + mdp + ", droit=" + droit + "]";
	}
	
	

}


D'ailleurs je ne sais toujours pas si j'ai le droit de créer un constructeur de Singleton avec des paramètres comme je le fais ...

public class Login extends JFrame {
	
	Connection cn;
	Statement st;
	ResultSet rs;
	
	
	JLabel labLogin, labPass;
	JPanel principal, panLogin, panPass, panButton;
	JTextField login, pass;
	JButton b;
	
	String user,password;
	
	static User utilisateur;
			
	
	public Login()
	{
		
		frame();
	}
	
	public void connect()
	{
		try
		{
			Class.forName("com.mysql.jdbc.Driver");
			System.out.println("Worked");
		}
		catch(ClassNotFoundException e)
		{
			e.printStackTrace();
		}
	}
	
	public void connectMySQL()
	{
		connect();
		String host = "jdbc:mysql://localhost/bdd";
		String user = "root";
		String pass = "";
		try
		{
			cn = DriverManager.getConnection(host, user, pass);
			st = cn.createStatement();
			/*PreparedStatement statement = (PreparedStatement) connect.prepareStatement("SELECT * FROM etudiant");
			
			ResultSet data = statement.executeQuery();
			while(data.next())
			{
				System.out.println("Name : "+data.getObject("nom")+ " Prenom : "+data.getObject("prenom"));
			}
			*/
			
			
			
			System.out.println("it works");
		}
		catch(SQLException e)
		{
			e.printStackTrace();
		}
	}
	
	public void deconnexion()
	{
		try
		{
			st.close();
			cn.close();
		}
		catch(SQLException e)
		{
			System.out.println(e.getMessage());
		}
		
	}
	
	public void frame()
	{
		this.setTitle("User Login");
		this.setPreferredSize(new Dimension(400, 220));
		this.setBackground(Color.WHITE);
		this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		this.setLayout(new BorderLayout());
		
		
		principal = new JPanel();
		principal.setBackground(Color.WHITE);

		panLogin = new JPanel();
		panLogin.setBackground(Color.WHITE);
		panLogin.setPreferredSize(new Dimension(220, 60));
		panLogin.setBorder(BorderFactory.createTitledBorder("User Login"));
		labLogin = new JLabel("Your login : ");
		login = new JTextField();
		login.setPreferredSize(new Dimension(100, 20));
		panLogin.add(labLogin);
		panLogin.add(login);
		
		panPass = new JPanel();
		panPass.setBackground(Color.WHITE);
		panPass.setPreferredSize(new Dimension(220, 60));
		panPass.setBorder(BorderFactory.createTitledBorder("User Password"));
		labPass = new JLabel("Your password : ");
		pass = new JTextField();
		pass.setPreferredSize(new Dimension(100, 20));
		panPass.add(labPass);
		panPass.add(pass);
		
		panButton = new JPanel();
		panButton.setBackground(Color.WHITE);
		b = new JButton("Login");
		b.addActionListener(new ActionListener() {
			
			@Override
			public void actionPerformed(ActionEvent e)
			{
				try
				{
					connectMySQL();
					user = login.getText().trim();
					password = pass.getText().trim();
					
					String sql = "SELECT login,pass FROM users WHERE login = '"+user+"'AND pass = '"+password+"'";
					rs = st.executeQuery(sql);
					
					
					int count = 0;
					
					while(rs.next())
					{
						count++;
					}
					
					if(count == 1)
					{
						JOptionPane.showMessageDialog(null, "User Found, Acces Granted");
					
					}
					else if(count > 1)
					{
						JOptionPane.showMessageDialog(null, "Duplicate User, Acces Denied");
					}
					else
					{
						JOptionPane.showMessageDialog(null, "User doesn't exist");
					}
					deconnexion();
					
				}
				catch(Exception ex)
				{
					ex.getMessage();
				}
				
			
			}
		});
		panButton.add(b);
		
		principal.add(panLogin);
		principal.add(panPass);
		
		
		this.getContentPane().add(principal, BorderLayout.CENTER);
		this.getContentPane().add(panButton, BorderLayout.SOUTH);
		pack();
		this.setVisible(true);
	}
	
	public void returnUser()
	{
		utilisateur = User.getInstance(user, password, "admin");
	}


Et voici le soucis dans le Main.java :
public class Main {
	public static void main(String[] args) {
		Login l = new Login();
		System.out.println(l.user);
	}
	
}



Je comprends que dans le main lorsque je souhaite afficher le User créé lors de la validation de la connexion, celui-ci soit null car le programme fait le new Login et en même temps avant que j'ai pu finir mon traitement dans ma frame, affiche le User donc il sera à null ...

Voilà je suis plus qu'ouvert à tous conseils que vous pourriez me donner, je débute vraiment dans ce domaine donc j'aimerai apprendre et avoir une bonne façon de faire ^^

PS: Désolé pour tout le code, la frame prend de la place ...

Merci d'avance !
A voir également:

3 réponses

KX Messages postés 16753 Date d'inscription samedi 31 mai 2008 Statut Modérateur Dernière intervention 25 novembre 2024 3 019
21 févr. 2016 à 11:04
Bonjour,

Créer un singleton avec des paramètres n'a pas de sens.

Tout les User vont utiliser le même singleton, donc si tu précises login/mdp à ton singleton, ça veut dire que tout les User auront le même login/mdp !

Exemple :

User toto = User.getInstance("toto", "123", "admin");
System.out.println(toto); // User [login=toto, mdp=123, droit=admin]

User tata = User.getInstance("tata", "789", "visitor");
System.out.println(tata); // User [login=toto, mdp=123, droit=admin]

System.out.println(toto == tata); // true

Dans ton code, un singleton pertinent pourrait être pour créer une unique connexion à la base de données,
DriverManager.getConnection
serait donc utilisé une seule fois pour te renvoyer toujours la même connexion (les user/mdp de base de données ne changent pas d'un appel à l'autre).
0
Hurobaki Messages postés 53 Date d'inscription dimanche 23 mars 2014 Statut Membre Dernière intervention 10 mars 2017
21 févr. 2016 à 11:20
Salut KX,

Merci de ta réponse, si je ne souhaite justement qu'il n'y ai qu'un seul utilisateur ce code serait-il correct ? La seule différence que je souhaite mettre en place serait si cet utilisateur est un admin ou non.

Mais je ne parviens pas à le récupérer dans mon Main.java car il est à null, il faudrait que j'attende d'avoir fini avec la frame pour ensuite continuer les instructions du Main.

Est-ce que cela est possible ? Ou est-ce qu'il y aurait une autre méthode ?
0
KX Messages postés 16753 Date d'inscription samedi 31 mai 2008 Statut Modérateur Dernière intervention 25 novembre 2024 3 019
21 févr. 2016 à 11:52
Un singleton ne doit pas dépendre d'une entrée interactive de l'utilisateur.

D'ailleurs tu as utilisé cette implémentation (correcte mais pas parfaite)

public class User {
	
	private static User instance;
	
	private User(...) { ... }
	
	public static synchronized User getInstance(...) {
		if (instance == null) {
			instance = new User(...);
		}
		return instance;
	}
}

Une autre implémentation du singleton (plus robuste) serait de faire :

public final class User {

	private static final User instance = new User(...);

	private User(...) { ... }

	public static User getInstance() {
		return instance;
	}
}

On voit clairement dans ce cas que les paramètre de getInstance() ne servent à rien, car au moment où tu appelles cette méthode, l'objet instance a déjà une valeur unique, qui ne peut donc pas dépendre des paramètres de getInstance()

Pour ce que tu veux faire exactement (je me suis un peu perdu dans ta classe Login, elle fait à la fois du Swing, de la DAO, c'est le bazar...) en gros ce que tu veux c'est rentrer le user/mdp dans une IHM et pouvoir l'utiliser ensuite ?

Dans ce cas il faudrait que ton main attende que la fenêtre soit fermée et donc plutôt utiliser une fenêtre Dialog qu'une Frame : How to Make Dialogs

En faisant bien, ton main pourrait ressembler à ça :
public static void main(String[] args) {
    LoginDialog dialog = new LoginDialog();
    int result = dialog.requestCredentials(); // demande user/mdp
    if (result == OK_OPTION) { // l'utilisateur appuie sur OK
        User user = dialog.getUser();
        boolean isValid = UserService.getInstance().isValid(user);
	if (isValid) { // on utilise l'application avec user
        }
        else { // on rejette l'accès à l'application
        }
    } else { // on rejette l'accès à l'application
    }
}
0
Hurobaki Messages postés 53 Date d'inscription dimanche 23 mars 2014 Statut Membre Dernière intervention 10 mars 2017
21 févr. 2016 à 12:05
Cette implémentation plus robuste, je ne doute pas qu'elle soit correcte mais pour ce qui est du multithread comment fait-elle pour palier ce problème ? Je me suis beaucoup renseigner et j'ai vu que le principal problème était le multithread sur un singleton, et que c'est pour cela qu'il fallait utiliser synchronized dans ma méthode.

Oui je mélange un peu à force chercher des solutions j'ai fais un gros mélange ...

public static void main(String[] args) {
    LoginDialog dialog = new LoginDialog();
    int result = dialog.requestCredentials(); // demande user/mdp
    if (result == OK_OPTION) { // l'utilisateur appuie sur OK
        User user = dialog.getUser();
        boolean isValid = UserService.getInstance().isValid(user);
	if (isValid) { // on utilise l'application avec user
        }
        else { // on rejette l'accès à l'application
        }
    } else { // on rejette l'accès à l'application
    }
}


Juste pour être sûr que j'ai compris, LoginDialog serait ma classe Login.
La méthode requestCredentials() est la Frame qui s'affiche pour demander le login/mdp. Lors du clic sur le bouton valider/ok, je stocke la valeur dans un entier => result et ce n'est qu'à ce moment là que je vais créer mon User.
Le seul soucis que j'ai c'est la fonction isValid, elle va ma permettre d'initialiser le login, mdp et droit de mon User ?
0
KX Messages postés 16753 Date d'inscription samedi 31 mai 2008 Statut Modérateur Dernière intervention 25 novembre 2024 3 019
21 févr. 2016 à 12:44
Pour les dialog ce n'est qu'un exemple. Mais oui LoginDialog c'est ce qui permettrait de gérer tes fenêtre de logins. Donc tu aurais un User user; que tu remplirais en fonction des différents champs. Et une ou plusieurs méthodes d'affichage de dialog, comme requestCredentials() pour demander user/mdp, ou initCredentials() pour créer un nouveau user, éventuellement resetCredentials()... Le plus important c'est qu'elle renvoie un entier pour dire quand l'utilisateur a validé la fenêtre, on attend cet entier pour continuer le programme.

La fonction isValid pour moi c'est la partie accès à la base de donnée, c'est ce qui va vérifier que le user/mdp saisi est correct ou non. Ce n'est pas à la fenêtre Swing de faire ce travail.

Une fois que tu as ton user/mdp et que tu as vérifié qu'il était bon tu peux faire par exemple
if (isValid) { startApplication(user); }
ce serait dans le démarrage que tu initialiserait le user pour ton application (normalement à ce moment là tu n'as plus besoin du mot de passe...)

Pour revenir au singleton, ma proposition fonctionne même en multithread, mais pour comprendre pourquoi ça marche il faut connaître le fonctionnement de Java :
En Java une classe n'est initialisée qu'au moment où elle est utilisée pour la première fois, par exemple la première fois où j'appelle un constructeur ou une méthode, ou si je force la création de la classe comme tu le fais avec
Class.forName
pour le driver.
Lors de l'initialisation, c'est le ClassLoader qui va récupérer le fichier .class et le mettre en mémoire, le chargement fait par le ClassLoader est threadsafe et il garantie que la classe est unique et que tout les champs static seront initialisés.
Dans notre cas, l'objet instance étant static l'appel au constructeur new User(...) qui lui donne sa valeur se fera donc lors du chargement de la classe par le ClassLoader.
On peux donc faire des
getInstance() { return instance; }
(pas besoin de synchronized), on est sûr que la valeur instance a une valeur et qu'elle est unique puisque la classe est unique aussi.

Le seul cas qui n'est pas géré (mais il n'est pas géré non plus avec ton implémentation) c'est que la classe est unique pour un ClassLoader donné. Quand on travaille avec plusieurs ClassLoader différent on pourrait avoir un singleton par ClassLoader avec donc des valeurs différentes.
Mais pour ton application il n'y a pas de problème, implicitement tu utilises toujours
ClassLoader.getSystemClassLoader()
donc le singleton sera bien unique.
0