Advertisement

How to Build a Full-Featured Login System

by

In this tutorial I will be showing you how to make a simple login system consisting of a login page, register page, forgotten password page, email activation, logout page and finally a users online page. I made this tutorial to mainly target new-to-PHP developers, due to the fact when I started I noticed the lack in quantity of basic login systems. Therefore, I decided to make one myself giving high quality advice on how to make your first login system with a users online script!


1. Making a Basic Stylesheet

We are going to create a very basic CSS stylesheet just to add a little bit of design and tidy up the way this login system looks. So too start off with open your text editor and we can begin making our styelsheet.

body { font-family: arial; font-size: 10pt; } table { font-size: 10pt; margin: 0 auto; } #border { border: 2px solid #999; background: #CCC; 
padding: 15px; margin: 0 auto; width: 300px; }

Save this file as style.css so we can link back to it whenever we need to. There we have our simple stylesheet! Now we can begin making our pages without having to worry too much about making them look reasonably good.



2. Creating the Login Page

Okay so we have a stylesheet defined, now it's time to get things displaying on our pages. Open a new file in your text editor, this is going to be our login.php page!

 <link rel="stylesheet" type="text/css" href="tut_files/style.css"> 
 
 <form action="login.php" method="post"> <div id="border"> <table border="0" cellpadding="2" cellspacing="0"> <tbody><tr> <td>Username:</td> <td><input name="username" type="text"></td> </tr> 
<tr> <td>Password:</td> <td><input name="password" type="password"></td> </tr> <tr> <td colspan="2" align="center"><input name="submit" value="Login" type="submit"></td> </tr> <tr> <td 
colspan="2" align="center"><a href="register.php">Register</a> | <a href="forgot.php">Forgot Pass</a></td> </tr> </tbody></table> </div> </form>

Okay this is what our login.php file will look like before we get our PHP making the login form function correctly. At the moment you will notice that it doesn't work. This is because we have not told the page what to do if the form is submitted.

Planning

Now let's do some planning before we dive into the PHP. We need to ask ourselves "What is the page going to be checking when the form is submitted?". For the login page here is a list of what we are going to be checking -

  • That both the username and password boxes have been filled in
  • That the username supplied exists in our database
  • That if the username exists in our database, the password matches the one for the username
  • Finally, that the user has activated their account

If the PHP can answer yes to all four of those points, then log the user in. Now in those four points you will notice there was a database mentioned. We are going to be using a MySQL database to store all of the information about each of our users. So before we get started on out PHP we need to make this database. At this point a bit more planning is needed. We need to decide what information we need to store about the users, what types of data are we storing, do we need a default value etc etc. Here is my plan below -

  • We need to store a username for the user which will be a varchar
  • We need a password to which will also be a varchar
  • We will need an email for our email activation function this can be varchar too
  • A field telling is if the account has been activated or not, this will be an integer
  • A field giving information about whether the user is online or not, this will be an integer
  • Finally, a field giving us a time the user registered, this is also an integer

Building the Database

Now from this we can see exactly how to build our table in our database. First create a database called loginTut. Then in this database we want to run the SQL I have provided below -

Note: All tables contain a primary key. This is a unique identifier for each row in the table!

CREATE TABLE IF NOT EXISTS `users` ( `id` int(11) NOT NULL auto_increment, `username` varchar(32) NOT NULL, `password` varchar(32) NOT NULL, 
`online` int(20) NOT NULL default ‘0', `email` varchar(100) NOT NULL, `active` int(1) NOT NULL default ‘0', `rtime` int(20) NOT NULL default ‘0', PRIMARY KEY (`id`) ) ENGINE=MyISAM DEFAULT 
CHARSET=utf8;

Now we have a table to store all the information we need about our users, let's add a user for testing purposes. To do this run the SQL provided below -

INSERT INTO `users` (`id`, `username`, `password`, `online`, `email`, `active`, `rtime`) VALUES (1, ‘testing’, ‘testing’, 0, 
‘fake@noemail.co.uk’, 0, 0);

So we now have one user with the username testing, the password testing and email fake@noemail.co.uk. Now we can get to the PHP and make out login form work!

Adding the PHP

First things first we need to think about security and how secure is this login form going to be. To help prevent SQL Injection which is a very common form of database hacking we are going to make a function that will protect all strings stored in the database. This we will put in an external file called functions.php. Here is the source -

 <?php 
 
function protect($string){ $string = trim(strip_tags(addslashes($string))); return $string; } 
 
?>

This function will trim our string (cut off any white space at the beginning or end of the string), strip tags (remove all html and PHP tags in the string), and then add slashes to the string escaping speech marks (’) and quotation marks (").

Back to login.php

Now we have a place to store and check user information from, a function to protect strings being passed to the database, and a nice looking layout for our login page! Below you can see the commented code for our login.php file with the newly added PHP-

 <link rel="stylesheet" type="text/css" href="tut_files/style.css"> 
 
 You need to fill in a <strong>Username</strong> and a <strong>Password</strong>!"; }else{ //if the were continue checking 
 
 //select all rows from the table where the username matches the one entered by the user $res = mysql_query("SELECT * FROM `users` WHERE `username` = '".$username."'"); $num = 
mysql_num_rows($res); 
 
 //check if there was not a match if($num == 0){ //if not display an error message echo "<center>The <strong>Username</strong> you supplied does not exist!</center>"; }else{ //if there was a match 
continue checking 
 
 //select all rows where the username and password match the ones submitted by the user $res = mysql_query("SELECT * FROM `users` WHERE `username` = '".$username."' AND `password` = 
'".$password."'"); $num = mysql_num_rows($res); 
 
 //check if there was not a match if($num == 0){ //if not display error message echo "<center>The <strong>Password</strong> you supplied does not match the one for that username!</center>"; }else{ //if 
there was continue checking 
 
 //split all fields fom the correct row into an associative array $row = mysql_fetch_assoc($res); 
 
 //check to see if the user has not activated their account yet if($row['active'] != 1){ //if not display error message echo "<center>You have not yet <strong>Activated</strong> your account!</center>"; 
}else{ //if they have log them in 
 
 //set the login session storing there id - we use this to see if they are logged in or not $_SESSION['uid'] = $row['id']; //show message echo "<center>You have successfully logged 
in!</center>"; 
 
 //update the online field to 50 seconds into the future $time = date('U')+50; mysql_query("UPDATE `users` SET `online` = '".$time."' WHERE `id` = '".$_SESSION['uid']."'"); 
 
 //redirect them to the usersonline page header('Location: usersOnline.php'); } } } } } 
 
 ?> <form action="login.php" method="post"> <div id="border"> <table border="0" cellpadding="2" cellspacing="0"> <tbody><tr> <td>Username:</td> <td><input name="username" type="text"></td> 
</tr> <tr> <td>Password:</td> <td><input name="password" type="password"></td> </tr> <tr> <td colspan="2" align="center"><input name="submit" value="Login" type="submit"></td> </tr> <tr> <td 
colspan="2" align="center"><a href="register.php">Register</a> | <a href="forgot.php">Forgot Pass</a></td> </tr> </tbody></table> </div> </form>

Most of this is explained by the commenting but one part I didn't explain is the online field. When you successfully login, we updated the online field to 50 seconds ahead of now. The date('U') function gives us a the amount of seconds since January 1 1970 00:00:00 GMT (Unix epoch). This means that date('U') will never get smaller, the value will always increase. If we set the online field to 50 seconds ahead of now then when the Users Online page is loaded we can check to find all the users where the online value is more than the time when the page is loaded, if this is the case then display each of their names.

Now feel free to test your login page. Make sure that all the checks are performed correctly and that once successfully logged in, you get redirected to the non existing users online page. You can also check to see if it has successfully updated the online field by checking your users table!



3. Creating the Register Page

What good is a login page without a register page? Not much at all so I think that will be the next step for us to take. Creating the register page is going to be very similar to creating our login page. We need to do some basic check to see if the username wanted is already taken, but there's nothing new happening there. Below you can see the commented register page code -

 <link rel="stylesheet" type="text/css" href="tut_files/style.css"> 
 
 You need to fill in all of the required filds!"; }else{ //if all were filled in continue checking 
 
 //Check if the wanted username is more than 32 or less than 3 charcters long if(strlen($username) > 32 || strlen($username) < 3){ //if it is display error message echo "<center>Your <strong>Username</strong> must be between 3 and 32 characters long!</center>"; }else{ //if not continue checking 
 
 //select all the rows from out users table where the posted username matches the username stored $res = mysql_query("SELECT * FROM `users` WHERE `username` = '".$username."'"); $num = 
mysql_num_rows($res); 
 
 //check if theres a match if($num == 1){ //if yes the username is taken so display error message echo "<center>The <strong>Username</strong> you have chosen is already taken!</center>"; }else{ 
//otherwise continue checking 
 
 //check if the password is less than 5 or more than 32 characters long if(strlen($password) < 5 || strlen($password) > 32){ //if it is display error message echo "<center>Your <strong>Password</strong> must be between 5 and 32 characters long!</center>"; }else{ //else continue checking 
 
 //check if the password and confirm password match if($password != $passconf){ //if not display error message echo "<center>The <strong>Password</strong> you supplied did not match the confirmation password!</center>"; }else{ //otherwise continue checking 
 
 //Set the format we want to check out email address against $checkemail = "/^[a-z0-9]+([_\\.-][a-z0-9]+)*@([a-z0-9]+([\.-][a-z0-9]+)*)+\\.[a-z]{2,}$/i"; 
 
 //check if the formats match if(!preg_match($checkemail, $email)){ //if not display error message echo "<center>The <strong>E-mail</strong> is not valid, must be name@server.tld!</center>"; }else{ //if they do, continue checking 
 
 //select all rows from our users table where the emails match $res1 = mysql_query("SELECT * FROM `users` WHERE `email` = '".$email."'"); $num1 = mysql_num_rows($res1); 
 
 //if the number of matchs is 1 if($num1 == 1){ //the email address supplied is taken so display error message echo "<center>The <strong>E-mail</strong> address you supplied is already taken</center>"; 
}else{ //finally, otherwise register there account 
 
 //time of register (unix) $registerTime = date('U'); 
 
 //make a code for our activation key $code = md5($username).$registerTime; 
 
 //insert the row into the database $res2 = mysql_query("INSERT INTO `users` (`username`, `password`, `email`, `rtime`) 
VALUES('".$username."','".$password."','".$email."','".$registerTime."')"); 
 
 //send the email with an email containing the activation link to the supplied email address mail($email, $INFO['chatName'].' registration confirmation', "Thank you for registering to us".$username.",\n\nHere is your activation link. If the link doesn't work copy and paste it into your browser address bar.\n\nhttp://www.yourwebsitehere.co.uk/activate.php?code=".$code, 'From:noreply@youwebsitehere.co.uk'); 
 
 //display the success message echo "<center>You have successfully registered, please visit you inbox to activate your account!</center>"; } } } } } } } } 
 
 ?> <div id="border"> <form action="register.php" method="post"> <table border="0" cellpadding="2" cellspacing="0"> <tbody><tr> <td>Username: </td> <td><input name="username" type="text"></td> 
</tr> <tr> <td>Password: </td> <td><input name="password" type="password"></td> </tr> <tr> <td>Confirm Password: </td> <td><input name="passconf" type="password"></td> </tr> <tr> <td>Email: 
</td> <td><input name="email" size="25" type="text"></td> </tr> <tr> <td colspan="2" align="center"><input name="submit" value="Register" type="submit"></td> </tr> <tr> <td colspan="2" 
align="center"><a href="login.php">Login</a> | <a href="forgot.php">Forgot Pass</a></td> </tr> </tbody></table> </form> </div>

New Functions

This file contains some new things you may not be familiar with, therefore I will go over everything. Firstly, the strlen() function, this returns the number of characters in a string allowing us to check how long strings are. Then the preg_match() function, this checks to see if the formatting of a string matches the formatting you specify (in this case being an email format). Finally the mail() function, this sends an email from the server to any email of your choice, containing anything you want. You should save this file as register.php

Now you can test you register page, you can see when you enter your correct email address you will receive an email with an activation link contained inside. You can also see that a row containing the data filled into the form is entered into the users table. The value of active is 0 showing that this account has not yet been activated!



4. Activate Your Account Page

This is only a small page with very little code required, but it's still very important and plays a huge role in a secure login system. The source for this page is shown below - activate.php

 <link rel="stylesheet" type="text/css" href="tut_files/style.css"> 
 
 Unfortunatly there was an error there!"; }else{ //other wise continue the check 
 
 //select all the rows where the accounts are not active $res = mysql_query("SELECT * FROM `users` WHERE `active` = '0'"); 
 
 //loop through this script for each row found not active while($row = mysql_fetch_assoc($res)){ //check if the code from the row in the database matches the one from the user if($code == md5($row['username']).$row['rtime']){ //if it does then activate there account and display success message $res1 = mysql_query("UPDATE `users` SET `active` = '1' WHERE `id` = '".$row['id']."'"); echo "<center>You have successfully activated your account!</center>"; } } } 
 
 ?>

There are two new things in this file, we use the GET method instead of POST and also we use a while() loop. The get method simply gets data from the address bar at the top of the user's browser (in this case being the code sent with the email to their email address). The while() loop is perfecting for checking through multiple rows of data selected from the database (in this case to see if there is a match with the codes).

Overview so Far

So far you should've learned many new things if your new to PHP and successfully created a half of a login system. The pages completed so far are -

  • style.css
  • functions.php
  • login.php
  • register.php
  • activate.php

Some useful functions used so far are -

  • mysql_connect() - Connect to a mysql database
  • mysql_select_db() - Select the database that we should work with
  • mysql_query() - Send querys to the database to get, insert or edit data
  • trim() - Cut unwanted white space of the beginning and end of a string
  • strip_tags() - Remove html and PHP tags from a string
  • addslashes() - Add slashes to s string allowing quotes and speech marks to be used safely
  • strlen() - Get the number of characters in a string
  • preg_match() - Preg match is to match the formatting of a string
  • mail() - Send mail from the server to the specified email address
  • md5() - This calculates the md5 hash of a string


5. Forgotten Your Password?

Next up is our forgotten password page. If the user forgets their password, we can email it to them now we know that they supplied a real email address because of the activation. So without further ado here's the commented code for forgot.php -

 <link rel="stylesheet" type="text/css" href="tut_files/style.css"> 
 
 You need to fill in your <strong>E-mail</strong> address!"; }else{ //else continue checking 
 
 //set the format to check the email against $checkemail = "/^[a-z0-9]+([_\\.-][a-z0-9]+)*@([a-z0-9]+([\.-][a-z0-9]+)*)+\\.[a-z]{2,}$/i"; 
 
 //check if the email doesnt match the required format if(!preg_match($checkemail, $email)){ //if not then display error message echo "<center><strong>E-mail</strong> is not valid, must be name@server.tld!</center>"; }else{ //otherwise continue checking 
 
 //select all rows from the database where the emails match $res = mysql_query("SELECT * FROM `users` WHERE `email` = '".$email."'"); $num = mysql_num_rows($res); 
 
 //check if the number of row matched is equal to 0 if($num == 0){ //if it is display error message echo "<center>The <strong>E-mail</strong> you supplied does not exist in our database!</center>"; }else{ //otherwise complete forgot pass function 
 
 //split the row into an associative array $row = mysql_fetch_assoc($res); 
 
 //send email containing their password to their email address mail($email, 'Forgotten Password', "Here is your password: ".$row['password']."\n\nPlease try not too lose it again!", 'From: noreply@yourwebsitehere.co.uk'); 
 
 //display success message echo "<center>An email has been sent too your email address containing your password!</center>"; } } } } 
 
 ?> <div id="border"> <form action="forgot.php" method="post"> <table border="0" cellpadding="2" cellspacing="0"> <tbody><tr> <td>Email: </td> <td><input name="email" type="text"></td> </tr> 
<tr> <td colspan="2" align="center"><input name="submit" value="Send" type="submit"></td> </tr> <tr> <td colspan="2" align="center"><a href="register.php">Register</a> | <a 
href="login.php">Login</a></td> </tr> </tbody></table> </form> </div>

This page consists of nothing new therefore I will spend less time looking over it. One thing I do want to mention is that if you haven't noticed because we have been including our css file into every page the layout we are using for each page is staying very similar keeping a nice smart design throughout the whole website.

The next and final page we will be doing in this tutorial will be slightly different. This page has the check to see if the user is logged in or not, and in this case displays all the users online at that current moment (or to be precise within the past 50 seconds).



6. The Users Online Page

Okay so we have made it to the section of the website you need to be logged in to view. As I mentioned before this one is going to be slightly different to the others because of the fact that we need to check if the user is logged in or not. If they are not logged in and try to view the page we have a few options we can do. The first being we can display an error message saying something along the lines of "You need to be logged in to view this page!", or we can redirect them back to the login page. For this tutorial I think I'm going to use the error message method.

So here is the usersOnline.php page's source -

 <link rel="stylesheet" type="text/css" href="tut_files/style.css"> 
 
 You need to be logged in to user this feature!"; }else{ //otherwise continue the page 
 
 //this is out update script which should be used in each page to update the users online time $time = date('U')+50; $update = mysql_query("UPDATE `users` SET `online` = '".$time."' WHERE `id` = '".$_SESSION['uid']."'"); ?> <div id="border"> <table width="100%" border="0" cellpadding="2" cellspacing="0"> <tbody><tr> <td><strong>Users Online:</strong></td> <td> '".date('U')."'"); 
 
 //loop for each row while($row = mysql_fetch_assoc($res)){ //echo each username found to be online with a dash to split them echo $row['username']." - "; } 
 
 ?> </td> </tr> <tr> <td colspan="2" align="center"><a href="logout.php">Logout</a></td> </tr> </tbody></table> </div>

As I mentioned, you can see this page is slightly different. Not only do we ensure that they are logged in, but we update the online time keeping the online field ahead of the current time. Each time a page is loaded with that script, it will update to put them online. Now we have one more final page to do and then we are done. Once a user has logged in, he needs to be able to log out!



Logout.php

This has to be considered the easiest page to make which I am sure most of you are glad to hear. Now here is the commented code for the logout.php file -

 <link rel="stylesheet" type="text/css" href="tut_files/style.css"> 
 
 You need to be logged in to log out!"; }else{ //if it does continue checking 
 
 //update to set this users online field to the current time mysql_query("UPDATE `users` SET `online` = '".date('U')."' WHERE `id` = '".$_SESSION['uid']."'"); 
 
 //destroy all sessions canceling the login session session_destroy(); 
 
 //display success message echo "<center>You have successfully logged out!</center>"; } 
 
 ?>

I think the comments in this file explain it enough, and I think your PHP knowledge now should be much higher and you should be able to understand most of this now.