Login and authentication class
I lately got maybe not the best but really helpful book about object oriented programming in php5 and I decided to write my own login and authentication class. In this post I will give you a short description, how I manage the authentication of users for restricted pages. This class will use information stored in MySQL table but I will comment it out for demo purposes. I assume that you know how to use MySQL database and you will be able to create the tables by yourself. If not please let me know and I will post also the table structure.
Here you can find a working demo of the class I will introduce in this example
Let's start with defining the class and the variables we will use in the scope of this class. We will give it a Login name because it has to handle a user, who wants to login to view certain content, which is available only for registered users.
class Login { private $user_name; private $user_pass; private $user_id; private $user_access; public $login_error; function __construct($username,$userpass) { $this->user_name = $username; $this->user_pass = (isset($_SESSION['user']])) ? $userpass : md5($userpass); } }
We declared the properties of this class as private, because we will use them only in scope of it and we will access them only internally from this class. The only public property will be responsible for error handling. We use __construct function to assign values to the properties we just declared, when the class will be initialized. We assume that password stored in database was encrypted using md5() function. As the first step we will declare a method, which will handle user login.
public function loginUser() { if($this->checkUser()) { $this->setUser(); header("Location:index.php?page=overview"); } else { $this->error_login = 'wrong username or password'; } }
We set the loginUser function to public so we can access it after we initialize our class. We check, if the user exists in our database. If the information in database matches the user input, we use setUser function to store the information about the user, which we can use later to authenticate or to get user content availably only for that certain user.
As second step we will declare a private method checkUser, which will check, if a the information provided by user exists in database. This will be a simple MySQL query and the method will return true in case user exists and false in case of any mismatch.
(In this place you might comment out MySQL check and just add hard coded check for certain string. I add this part as a commentary.)
private function checkUser() { /* return ($this->user_name == 'demo' && $this->user_pass == 'demo') ? true : false; */ $sql = sprintf("SELECT COUNT(*) AS exist FROM usertable WHERE username='%s' AND pass='%s' AND confirmed='1';",$this->user_name,$this->user_pass); $res = mysql_query($sql) or die(mysql_error()); return (mysql_result($res,0,'exist') == 1) ? true : false; }
In the next step we will define a method, which will set our user as a session array, where we will store all the important information about our user.
(Again, if you don't want to use database, you can define default user id.)
private function setUser() { /* $this->user_ip = 1; */ $sql = sprintf("SELECT id FROM miss_user WHERE username='%s' AND pass='%s';",$this->user_name,$this->user_pass); $res = mysql_query($sql) or die(mysql_error()); $this->user_id = mysql_result($res,0,'id'); $_SESSION['user'] = array('user_id'=>$this->user_id, 'user_name'=>$this->user_name, 'user_pass'=>$this->user_pass, 'user_ip'=>$_SERVER['REMOTE_ADDR']); setcookie('userip',$_SERVER['REMOTE_ADDR'],0); }
As you can notice we saved user id, user name, user password and user remote address in an array, which we will later use to authenticate user. We declared this method as private again, because we will access it only in the scope of our class. Additionally we save a cookie with the IP address of the user to perform a check against session stealing. Even if someone would try to steal the session by just randomly typing it into the URL address, the authentication will be failed because it wont match the IP in the cookie.
In this step we will actually perform the authentication of a user.
public function authenticateUser($user) { if(is_array($user) && !empty($user)) { if($this->checkUser()) { return ($_COOKIE['userip'] == $_SESSION['user']['user_ip']) ? true : false; } else { return false; } } else { return false; } }
First we check, if the session array is an array at all and if it's not empty. After that we perform checkUser again to compare the session array with the database information. At the end we do the last check, if the IP address saved in the cookie matches the address saved in session array. In my case the method authenticateUser returns true on success and false in case of failure but you can do whatever you want after a user passes authentication. We save id of a user because it might be useful later in getting the information relevant only for this certain user or from two MySQL tables, which are related to each other by an user ip.
The only thing, which we are missing right now is the log out method.
public function logOut() { unset($_SESSION['user']); setcookie('userip','',time()-3600); header("Location:login.php"); }
I think this is self explanatory. We unset the sessionn, destroy the cookie and redirect the user to the login page. That would be all. Now i will present a simple usage of this class.
session_start(); error_reporting(E_ALL); include('class.login.php'); if(isset($_POST['submit']) && $_POST['submit'] == 'login') { $login = new Login($_POST['username'],$_POST['pass']); $login->loginUser(); } elseif(isset($_SESSION['user']) && is_array($_SESSION['user']) && !empty($_SESSION['user'])) { $login = new Login($_SESSION['user']['user_name'],$_SESSION['user']['user_pass']); } if(isset($_GET['page']) && $_GET['page'] == 'logout') { $login->logOut(); }
We start a session and include our class before we send any headers. We use error_reporting only for develpment reasons and you can erase this line or decrees the level of error reporting if you want. We perform a check, if user submit the login form. If yes we initialize the Login class with the information provided by user. If the input validates we log in user and redirect him to welcome page (as stated in the class). If the user didn't submit the login information we perform another check, if the session array is set and if it's not empty we initialize our class with the information saved in our session array. If the user clicks on the log out button, we use logOut method to destroy all the information about the user. After that we can send some headers.
<?php if(isset($_SESSION['user']) && $login->authenticateUser($_SESSION['user'])) { if(isset($_GET['page']) && $_GET['page'] == 'logged') { ?> <a href="login.php?page=logout">Logout</a> <?php } } else { echo (isset($_POST['submit'])) ? $login->error_login : ''; ?> <form action="<?=$_SERVER['PHP_SELF']?>" method="post"> username:<input name="username" type="text" /> password:<input name="pass" type="text" /> <input name="submit" type="submit" value="login" /> </form> <?php } ?>
In this part we perform actual authentication of the user and display information for authenticated users and if there is any error we display the error message
Wow I did it. It's my first tutorial kind of example thing:) If you want to download the files with source code for this example you can do it here. I'm waiting for your feedback right now. If you went that far you can write at least couple words:) Going back to work now...
December 28th, 2008 - 23:42
Great post, but could you publish the complete (*.zip archive) source codes of your examples under your posts? Thanks a lot!
December 31st, 2008 - 12:44
Hi raketoplan2005,
Currently I’m pretty busy with work, but as soon as I will find some time I will publish source codes for other examples as well.
April 10th, 2009 - 11:34
Hi!
Good post! The only problem when you try to check user in auth.php :
$login = new Login($_SESSION['user']['user_name'],$_SESSION['user']['user_pass']);
if($login->authenticateUser($_SESSION['user'])) {
you post the session user_pass which is already in md5() and the __construct try to encrypt it again, so it won’t find the right data in db.
April 18th, 2009 - 00:21
Excellent, I have used this code and logic for login and authentication. Really i am very excited feelings from this code because it was very helpful for me.
April 19th, 2009 - 11:48
Hi damn,
that’s a really good observation. The fastest way to fix that would be to change the constructor and replace the line where we set the password with this one:
$this->user_pass = (isset($_SESSION['user'])) ? $userpass : md5($userpass);
I’ll change the source. Thanks
April 19th, 2009 - 11:57
@Shahalam
I’m glad to hear that