Jump to content


Check out our Community Blogs

Register and join over 40,000 other developers!


Recent Status Updates

View All Updates

Photo
- - - - -

Java Login Dialog [Part 2]

login

  • Please log in to reply
1 reply to this topic

#1 lethalwire

lethalwire

    while(false){ ... }

  • Senior Member
  • PipPipPipPipPipPip
  • 766 posts
  • Programming Language:C, Java, PHP, JavaScript
  • Learning:PHP

Posted 31 December 2011 - 09:10 PM

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 XAMPP tutorials 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.
DatabaseDesign.png

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

Hashed Password: 5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8
Screenshot of database:
databaseEntry.png

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
buildPathEclipse.png

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:
BuildPathConnectorJ.png

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) {
                    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()) {
				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 {
            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 )
			return;
		try {
			if( conn.isClosed() )
				return;
			conn.close();
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}
}
successfulLogin.png

>>Link To Source Code for Parts 1 & 2<<

Edited by lethalwire, 01 January 2012 - 11:26 AM.
Modified Statements into PreparedStatements

  • 0

#2 Alexander

Alexander

    YOL9

  • Moderator
  • 3963 posts
  • Location:Vancouver, Eh! Cleverness: 200
  • Programming Language:C, C++, PHP, Assembly

Posted 31 December 2011 - 10:47 PM

A great tutorial. A word of caution however, if you feed variables directly in to the query string you can enter something like this to bypass all security:
username = "abc'; UPDATE users SET Password='foo' WHERE Username='admin" (can be much shorter)
resulting query = "SELECT Password FROM users WHERE Username='abc'; UPDATE users SET Password='foo' WHERE Username='admin'";

One would probably want to employ prepared statements via the PreparedStatement object.

"encryption" for your SHA-1 operation is probably not a good description, as you are rather mapping it to a smaller data set. It would be cryptographic hashing, with a hash function. It sometimes does not matter, however it is better to separate the two terms.
  • 0

All new problems require investigation, and so if errors are problems, try to learn as much as you can and report back.






Also tagged with one or more of these keywords: login

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download