Full CakePHP 1.2 App 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.

class UsersController extends AppController {
	// name variable
	var $name = 'Users';

	// load any helpers used in the views
	var $helpers = array('Html', 'Form');

	/**
	 * index()
	 * main page for users
	 * url: /users/index
	 */
	function index() {
		// redirect to login page
		$this->redirect('login');
	}

	/**
	 * index()
	 * login page for users
	 * url: /users/login
	 */
	function login() {

	}

	/**
	 * logout()
	 * logs out a user
	 * url: /users/logout
	 */
	function logout() {

	}
}

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.

// file: /app/views/users/login.ctp

<div class="login form">

<?php if(isset($error)): ?>
<p class="flash_bad">
	<?php e($error); ?>
</p>
<?php endif; ?>

<?php echo $form->create('User', array('url'=>'login'));?>
	<fieldset>
 		<legend>Login</legend>
		<div class="input required">
			<label>Username: *</label>
			<?php echo $form->text('User.username'); ?>
		</div>
		<div class="input required">
			<label>Password: *</label>
			<?php echo $form->password('User.password'); ?>
		</div>
	</fieldset>
<?php echo $form->end('Login');?>

</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.

// file: /app/controllers/users_controller.php

function login() {
	// if the form has been submitted
	if(!empty($this->data)) {
		// check the username and password
		if( ($user = $this->User->check_login($this->data)) ) {
			// save the user information to the session
			$this->Session->write('User', $user);
			// set flash messsage
			$this->Session->setFlash('You have successfully logged in.', 'flash_good');
			// redirect the user
			$this->redirect('/admin/dvds/');
		} else {
			// set error message
			$this->set('error', 'ERROR: Invalid Username or Password.');
		}
	}
}

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.

<?php
// file: app/model/user.php

class User extends AppModel {
	var $name = 'User';
	var $useTable = 'admins';

	/**
	 * check_login()
	 * checks the username and password against the database
	 */
	function check_login($data) {
		// init
		$valid = false;
		
		// find user from the database with the username
		$user = $this->find('first', array(
			'conditions' => array(
				'User.username'=>$data['User']['username']
			)
		));
		
		// if the passwords match
		if($user['User']['password'] == md5($data['User']['password'])) {
			$valid = $user;
		}
		
	return $valid;
	}
}
?>

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.

// file: /app/controllers/dvds_controller.php

/**
 * beforeFilter()
 * this is called before any action in the controller
 */
function beforeFilter() {
	// if an admin action has been called
	if(isset($this->params['admin']) && $this->params['admin']) {
		// check that a user is logged in
		$this->check_user();
	}
}

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.

// file: /app/app_controller.php

/**
 * check_user()
 * checks that a user is logged in
 */
function check_user() {
	// get user data from session
	$user = $this->Session->read('User');

	// if empty
	if(empty($user)) {
		// set a flash message
		$this->Session->setFlash('ERROR: You must be logged to access the admin area.', 'flash_bad');
		// redirect user
		$this->redirect(array('action'=>'login', 'controller'=>'users', 'admin'=>false));
	}
}

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.

// file: /app/controllers/users_controller.php

function logout() {
	// delete the User session
	$this->Session->delete('User');
	// set flash message
	$this->Session->setFlash('You have successfully logged out.', 'flash_good');
	// redirect the user
	$this->redirect(array('action'=>'login', 'controller'=>'users')); 
}

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! :)

Next Article

In the next article I'm going to be designing the front-end of the application using photoshop and coding up the design in valid HTML and CSS. I usually hit some of the big CSS design galleries for inspiration before starting a new layout, draw up some ideas using old school pencil and paper and finally open up photoshop to play around with the look I want. Check back shorty and if you've any questions leave a comment. Cheers.

Posted on 22nd May 2008
9 years, 1 month, 5 days ago

comments powered by Disqus