Full CakePHP Application Part 6


CakePHP Login Screen
In this article I'm going to create a simple user authentication system whereby users will have to login to access any admin controller action. This is quite a basic application so I'm using the Session route of identifying a logged in user instead of the ACL route.

Users Controller

The Users Controller will deal with logging in and logging out users from the system so I'm going to have a separate action for each, I'm also going to include an index action which will just redirect the user to the login page.
  1. class UsersController extends AppController {  
  2.     // name variable  
  3.     var $name = 'Users';  
  4.   
  5.     // load any helpers used in the views  
  6.     var $helpers = array('Html''Form');  
  7.   
  8.     /** 
  9.      * index() 
  10.      * main page for users 
  11.      * url: /users/index 
  12.      */  
  13.     function index() {  
  14.         // redirect to login page  
  15.         $this->redirect('login');  
  16.     }  
  17.   
  18.     /** 
  19.      * index() 
  20.      * login page for users 
  21.      * url: /users/login 
  22.      */  
  23.     function login() {  
  24.   
  25.     }  
  26.   
  27.     /** 
  28.      * logout() 
  29.      * logs out a user 
  30.      * url: /users/logout 
  31.      */  
  32.     function logout() {  
  33.   
  34.     }  
  35. }  

Logging In

The first thing I need to create is a login view that will display the form so that a user can enter a username and password. I've taken a different approach to this form and manually created the surrounding div tag and form label instead of using the $form->input() to build everything for me.
  1. // file: /app/views/users/login.ctp  
  2.   
  3. <div class="login form">  
  4.   
  5. <?php if(isset($error)): ?>  
  6. <p class="flash_bad">  
  7.     <?php e($error); ?>  
  8. </p>  
  9. <?php endif; ?>  
  10.   
  11. <?php echo $form->create('User'array('url'=>'login'));?>  
  12.     <fieldset>  
  13.         <legend>Login</legend>  
  14.         <div class="input required">  
  15.             <label>Username: *</label>  
  16.             <?php echo $form->text('User.username'); ?>  
  17.         </div>  
  18.         <div class="input required">  
  19.             <label>Password: *</label>  
  20.             <?php echo $form->password('User.password'); ?>  
  21.         </div>  
  22.     </fieldset>  
  23. <?php echo $form->end('Login');?>  
  24.   
  25. </div>  
The login() action will take the form input and pass it to a custom function in the User Model that will try to find a user in the database with the username and will compare the passwords. If a user was found and the passwords match then the user information will be returned as a result. If either the username doesn't exist or the password is wrong the function will return false.
If the username/password are both ok then I'm going to save the user data in a session and I will use this session to check that a user is logged in. After the session is saved the user is redirected to the admin_index() of the DVDs Controller.
  1. // file: /app/controllers/users_controller.php  
  2.   
  3. function login() {  
  4.     // if the form has been submitted  
  5.     if(!empty($this->data)) {  
  6.         // check the username and password  
  7.         if( ($user = $this->User->check_login($this->data)) ) {  
  8.             // save the user information to the session  
  9.             $this->Session->write('User'$user);  
  10.             // set flash messsage  
  11.             $this->Session->setFlash('You have successfully logged in.''flash_good');  
  12.             // redirect the user  
  13.             $this->redirect('/admin/dvds/');  
  14.         } else {  
  15.             // set error message  
  16.             $this->set('error''ERROR: Invalid Username or Password.');  
  17.         }  
  18.     }  
  19. }  
If the login attempt was unsuccessful then the form will be displayed with an appropriate error message:
CakePHP Login Error Screen

The User Model

The User Model is setup to use the "admins" table in the database. This table will store the username and an md5 hashed password of all the users that will have access to the application. The "useTable" variable is used to change the default table that the Model connects to.
The check_login() functions takes the form data passed from the Controller and tries to find the user in the database. The passwords are then compared and if everything was ok the user data from the database is passed back to the Controller.
  1. <?php  
  2. // file: app/model/user.php  
  3.   
  4. class User extends AppModel {  
  5.     var $name = 'User';  
  6.     var $useTable = 'admins';  
  7.   
  8.     /** 
  9.      * check_login() 
  10.      * checks the username and password against the database 
  11.      */  
  12.     function check_login($data) {  
  13.         // init  
  14.         $valid = false;  
  15.           
  16.         // find user from the database with the username  
  17.         $user = $this->find('first'array(  
  18.             'conditions' => array(  
  19.                 'User.username'=>$data['User']['username']  
  20.             )  
  21.         ));  
  22.           
  23.         // if the passwords match  
  24.         if($user['User']['password'] == md5($data['User']['password'])) {  
  25.             $valid = $user;  
  26.         }  
  27.           
  28.     return $valid;  
  29.     }  
  30. }  
  31. ?>  
If the login attempt was a success the user is redirected to the admin index page of the DVDs Controller with a flash message:
CakePHP Login Success Screen

Checking a User is Logged In

In order to check that a user is logged in to the application I can check in the Session for a variable called "User". If it exists I can assume the user has successfully logged in, if not I can redirect the user to either the login page or the main DVDs page.
In all the Controllers I can use the beforeFilter() function, which will run before any of the actions in the Controller are processed. This enables me to call a function that will check for the Session variable. The params['admin'] is just a global array that will indicate if an admin action has been requested and if so I want to check that a user is logged in.
  1. // file: /app/controllers/dvds_controller.php  
  2.   
  3. /** 
  4.  * beforeFilter() 
  5.  * this is called before any action in the controller 
  6.  */  
  7. function beforeFilter() {  
  8.     // if an admin action has been called  
  9.     if(isset($this->params['admin']) && $this->params['admin']) {  
  10.         // check that a user is logged in  
  11.         $this->check_user();  
  12.     }  
  13. }  
I'm going to place the check_user() function inside the app_controller.php file so that I can call it from all of my Controller files. This function will read the "User" variable for the Session and if its empty the user will be redirected to the login page with a flash error message.
  1. // file: /app/app_controller.php  
  2.   
  3. /** 
  4.  * check_user() 
  5.  * checks that a user is logged in 
  6.  */  
  7. function check_user() {  
  8.     // get user data from session  
  9.     $user = $this->Session->read('User');  
  10.   
  11.     // if empty  
  12.     if(empty($user)) {  
  13.         // set a flash message  
  14.         $this->Session->setFlash('ERROR: You must be logged to access the admin area.''flash_bad');  
  15.         // redirect user  
  16.         $this->redirect(array('action'=>'login''controller'=>'users''admin'=>false));  
  17.     }  
  18. }  
If an attempt is made to access an admin action by a user that isn't logged in then the user will be redirected with an error message:
CakePHP Need to be Logged In Screen Make sure you place the beforeFilter() method inside any Controller that you want to restrict access to or alternatively you can place it inside your app_controller.php file to make it run before every Controller in your application which is quite useful.

Logging Out

Because I'm using the user data in the Session to determine if a user is logged in or not I can simply delete this variable from the Session and redirect the user to the login page. The logout action does not require a View file because there is nothing to do display, the user is simply redirected.
  1. // file: /app/controllers/users_controller.php  
  2.   
  3. function logout() {  
  4.     // delete the User session  
  5.     $this->Session->delete('User');  
  6.     // set flash message  
  7.     $this->Session->setFlash('You have successfully logged out.''flash_good');  
  8.     // redirect the user  
  9.     $this->redirect(array('action'=>'login''controller'=>'users'));   
  10. }  

Wrapping Up

This has been a very simple way to create an admin section with CakePHP, you could use the more advanced ACL or Auth Component but I haven't had much experience with either so for a simple online application this authentication will be sufficient. I've covered writing to and reading from CakePHP Sessions, creating a custom function in a Model and using the beforeFilter() function to run a bit of logic before any action in the Controller.
CakePHP has a number of other callback functions along with beforeFilter() and these include:
  • beforeRender()
  • afterFilter()
  • afterRender()
For more infomation about these check out the Controllers section of the cookbook. Models also have callback functions so check those out as well.

Source Code

The source code for this article can be downloaded using this link. If these articles are helping you out why not consider donating I can always use a beer! :)

This entry was posted in . Bookmark the permalink.

Leave a Reply