Java Login Dialog [Part 2]

After finishing Part 1 of the tutorial, you may be asking yourself how you can expand the code and actually login to something.
That something we'll be logging into is a database. 

The idea is to take the supplied information (username & password) and verify that the given password matches the given username in the database. 



Objectives:
Finish the login dialog so when the user attempts to login, the information is verified against information taken from the database. If the information matches then the user has successfully logged in. Otherwise, the user has either supplied a wrong username and/or password. 

Prerequisites:
Again, a fair amount of knowledge is expected to begin this tutorial. Some new concepts you'll see are:
  • Java Database Connections (JDBC)
  • MySQL
  • MySQL Connector/J
  • Statements
  • ResultSets
  • SHA-1 Hashing Function
  • XAMPP
  • PhpMyAdmin
I've setup a database using PhpMyAdmin. You can follow the XAMPPtutorials for a quick and painless solution that will setup a local server with MySQL capabilities. 

Setting up the Database:

I created and set the database name to logins. 
In the logins database I've create 1 table named users. The users table has 3 columns:
  • UserID (Primary Key) (Auto-increment) (Integer)
  • Username (Text) (Length = 20)
  • Password (Binary) (Length = 20)
The first word is the column name. Following the column names are any special options that have been set for each column. 
Attached File  DatabaseDesign.png   5.45K   60 downloads

I have entered a username and hashed password to the database.
Username: username
Password: password

Hashed Password: 5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8
Screenshot of database:
Attached File  databaseEntry.png   4.62K   44 downloads

This is the data we'll be using to compare with the end-user's login data.

Setting up the Mysql Connector/J Driver
This driver enables the client to be able to speak with the MySQL database
First download the driver from the following website:
MySQL :: Download Connector/J

Once downloaded, unzip the file until you gain access to the file named:
mysql-connector-java-5.1.18-bin.jar

I created a folder named libraries and place this file inside of it. 
You'll need to be sure to include this library in your classpath.

In eclipse, you can Right Click the Project and go to BuildPath then Configure BuildPath
Attached File  buildPathEclipse.png   54.59K   46 downloads

Under the Libraries tab, click the button titled Add External JARs...Next navigate to the location where you saved mysql-connector-java-5.1.18-bin.jar and choose open

Here's a screenshot of what the buildpath looks like after including the driver:
Attached File  BuildPathConnectorJ.png   68.18K   53 downloads

Step 1 - Decide Which Classes Are Relevant For Your Objective
Step 2 - Filling in the Missing Body of the loginButton's ActionListener
The idea is straight forward. 
When the loginButton is clicked, the usernameField's text is stored into a String called username
The password is retrieved from the passwordField and stored into a byte[] array using the UTF-8 charset. 

A boolean named success is used to determine if the login attempt was successful or not.
On success, a message is shown telling the end-user that the login was successful and the dialog closes.
Otherwise, an unsuccessful message is shown and the dialog remains open. 

                buttons[0].addActionListener( new ActionListener() {

                       
public void actionPerformed(ActionEvent e) {

                               
String username = usernameField.getText().trim();

                               
byte[] password = null;

                               
try {

                                        password
= new String(passwordField.getPassword()).trim().getBytes("UTF-8");

                               
} catch (UnsupportedEncodingException e1) {

                                        e1
.printStackTrace();

                               
}

                                success
= [COLOR=#ff0000]submit(username, password);[/COLOR]

                               
if(success) {

                                  &nb
sp;    
JOptionPane.showMessageDialog(null, "Successful login");

                                        close
();

                               
} else {

                                       
JOptionPane.showMessageDialog(null, "Incorrect username or password");

                               
}

                       
}

               
});

Step 3 - Creating the submit(String username, byte[] password) Method
In this method, the username and password lengths are checked and if they are less than 1, the method returns false.
In any other case, the method passes these parameters to the hashAndVerify(...) method which hashes the password and verifies it against the correct database information.

       
protected boolean submit(String username, byte[] password) {

               
if(username.length() < 1 || password.length < 1)

                       
return false;

               

               
return hashAndVerify(username, password);

       
}

Step 4 - Creating the hashAndVerify(String username, byte[] password) Method
The idea is to obtain a hash value for the given password using the SHA-1 algorithm and compare it to the hashed password gathered from the database.
If the data matches, then the login attempt is successful. 

The first line in this method looks like:
byte[] hashedPassword = hash(password);

The hashed password is now stored in the previous byte[] array. (The hash(...) method will be shown later)

The entire body looks like so:

       
private boolean hashAndVerify(String username, byte[] password) {

               
byte[] hashedPassword = hash(password);


               
String query = "SELECT Password FROM users WHERE Username = ?";

               
PreparedStatement stmt = DatabaseConnection.getStatement(query);

               
ResultSet rs = null;

               
try {

                        stmt
.setString(1, username);

                        rs
= stmt.executeQuery();

                       
while (rs.next()) {

                              &n
bsp;
byte[] arr = rs.getBytes("Password");

                               
if (java.util.Arrays.equals(hashedPassword, arr)) {

                                       
return true;

                               
}

                       
}

               
} catch (SQLException e1) {

                        e1
.printStackTrace();

               
} finally {

                       
try {

                               
if (rs != null)

                                        rs
.close();

                               
if (stmt != null)

                                        stmt
.close();

                               
DatabaseConnection.closeConnection();

                       
} catch (SQLException e) {

                                e
.printStackTrace();

                       
}

               
}


               
return false;

       
}

Using the DatabaseConnection's (defined later) static method getStatement(), we're able to get a ResultSet from the following query:

String query = "SELECT Password FROM users WHERE Username='" + username + "'";
The password from the database is temporarily stored into a byte[] array named arr
If the hashed password and the password retrieved from the database match then method returns true.
If not, then the method returns false.

Step 5 - Constructing the hash(byte[] password) Method

First, the MessageDigest sha1 object is initialized with the SHA-1 algorithm.
The sha1 object then obtains a hashed value for the password. The returned value is the hashed password in a byte[] array. 


       
private byte[] hash(byte[] password) {

               
MessageDigest sha1 = null;

               
try {

              &nb
sp;         sha1
= MessageDigest.getInstance("SHA-1");



               
} catch (NoSuchAlgorithmException e) {

                        e
.printStackTrace();

               
}



               
if (sha1 == null)

                       
return null;



                sha1
.update(password);

               
return sha1.digest();

       
}

Here is the DatabaseConnection.java class used to maintain the Connection to the database:

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class DatabaseConnection {


       
private static final String DATABASE_URL = "jdbc:mysql://localhost:3306/logins";

       
private static final String USERNAME = "root";

       
private static final String PASSWORD = "";

       
private static Connection conn;

       

       
private DatabaseConnection() { }

       

       
public static PreparedStatement getStatement(String query) {

               
PreparedStatement stmt = null;

               

               
try {

                         stmt
= getConnection().prepareStatement(query);

               
} catch (SQLException e) {

                        e
.printStackTrace();

               
}

               

               
return stmt;

       
}

       

       
private static Connection getConnection() {

               
try {

                       
if(conn == null || conn.isClosed() )

                                conn
= DriverManager.getConnection(DATABASE_URL, USERNAME, PASSWORD);

               
} catch (SQLException e) {

                        e
.printStackTrace();

               
}

               
return conn;

       
}

       

       
public static void closeConnection() {

               
if(conn == null )

                    &nbsp
;  
return;

               
try {

                       
if( conn.isClosed() )

                               
return;

                        conn
.close();

               
} catch (SQLException e) {

                        e
.printStackTrace();

               
}

       
}
}

No comments:

Post a Comment