欢迎各位兄弟 发布技术文章

这里的技术是共享的

You are here

User Authentication with Laravel

shiping1 的头像

User Authentication with Laravel

Almost every web application has some need for authentication. Users of our app need the ability to login and logout. Fortunately, Laravel provides a clean, simple and unobtrusive password-based authentication class to help you validate user credentials and retrieve information about the current user of your application.

This chapter will guide you through the process of setting up user logins,

Let’s start with a quick scenario of how a typical user authentication process works:

LoginAllow a user to sign in with her/his valid security credentials. The authentication process happens by matching the uniquely identifiable information such as username, email address and password in the database, allowing the user access to the protected resources only if the given information matches the stored values successfully. If unsuccessful, the user will be redirected to the login page again.
LogoutAllow the user to sign out and set the authenticated user session to nil.
Access RestrictionCheck the session to see if the current user is logged in. Deny access to protected resources if the user is not authenticated.

Fortunately, Laravel provides a robust implementation in the form of the Auth class. The Auth class has several methods:

Laravel MethodPurpose
Auth::check()Returns true if the current user is logged in, false otherwise.
Auth::guest()Returns true if the current user is not logged in (a guest).
Auth::user()Get the currently logged in user.
Auth::attempt(array $credentials = array(), $remember = false)Attempt to authenticate a user using the given credentials. The $remember parameter denotes if the user should be permanently “remembered” by the application.
Auth::login(UserInterface $user, $remember = false)Log a user into the application.
Auth::logout()Log the user out of the application.

What We Are Making

Note: Source code for this demo project is available at the github repository

Setup

We will begin by installing Laravel 4 as described in the previous lessons.

Installing Meido HTML and Form Helpers

For our project, we will need a few helper libraries to assist building HTML pages and forms. We will use the Meido HTML and Meido Form libraries. These two libraries are port of Laravel 3’s HTML and Form helper classes. We will leverage composer to automatically install these libraries for us. Open up the composer.json file (in your Laravel app folder) and add the strings "meido/form": "1.0.*" and "meido/html": "1.0.*" to the require section. Here’s what the relevant section will look like:

"require": {
        "illuminate/foundation": "1.2.*",
        "meido/form": "1.0.*",
        "meido/html": "1.0.*"
},

Execute the command composer update from the terminal and let composer install all the dependencies for us.

Now, we’ll need to let Laravel to acknowledge these helper libraries. Open up the app/config/app.php file and add the following lines to the providers section:

'Meido\HTML\Providers\HTMLServiceProvider',
'Meido\Form\Providers\FormServiceProvider',

So that the providers section will look like the following:

'providers' => array(
        ...
        'Meido\HTML\Providers\HTMLServiceProvider',
        'Meido\Form\Providers\FormServiceProvider',

We will also need to add the following line to the aliases section of the same file:

'Form' => 'Meido\Form\Facades\Form',
'HTML' => 'Meido\HTML\Facades\HTML',

So that the aliases section will look like the following:

'aliases' => array(
        ...
        'Form' => 'Meido\Form\Facades\Form',
        'HTML' => 'Meido\HTML\Facades\HTML',

Database Setup

We will begin by creating a new database named laravel_auth in our MySql server. We are going to update ourapp/config/database.php accordingly:

'default' => 'mysql',

'connections' => array(
        'mysql' => array(
                'driver'    => 'mysql',
                'host'      => '127.0.0.1',
                'database'  => 'laravel_auth',
                'username'  => '<YourDatabaseUsername>',
                'password'  => '<YourDatabasePassword>',
                'charset'   => 'utf8',
                'collation' => 'utf8_unicode_ci',
                'prefix'    => '',
        ),
),

Let’s look over the app/config/auth.php file. The authentication configuration contains some basic options to help you get started with authentication.

<?php

return array(

        /*
        |--------------------------------------------------------------------------
        | Default Authentication Driver
        |--------------------------------------------------------------------------
        |
        | This option controls the authentication driver that will be utilized.
        | This drivers manages the retrieval and authentication of the users
        | attempting to get access to protected areas of your application.
        |
        | Supported: "database", "eloquent"
        |
        */

        'driver' => 'eloquent',

        /*
        |--------------------------------------------------------------------------
        | Authentication Model
        |--------------------------------------------------------------------------
        |
        | When using the "Eloquent" authentication driver, we need to know which
        | Eloquent model should be used to retrieve your users. Of course, it
        | is often just the "User" model but you may use whatever you like.
        |
        */

        'model' => 'User',

        /*
        |--------------------------------------------------------------------------
        | Authentication Table
        |--------------------------------------------------------------------------
        |
        | When using the "Database" authentication driver, we need to know which
        | table should be used to retrieve your users. We have chosen a basic
        | default value but you may easily change it to any table you like.
        |
        */

        'table' => 'users',

);

Let’s discuss the auth.php settings in detail:

driver

Laravel’s authentication mechanism is “driver based”, meaning the responsibility for retrieving users during authentication is delegated to various “drivers”. Two are included out of the box: “Eloquent” and “Database”.

The Eloquent driver uses the Eloquent ORM to load the users of your application, and is the default authentication driver. The Database driver uses the Laravel fluent query builder to interact with the database and load your users.

model

When using the Eloquent authentication driver, this option determines the Eloquent model that should be used when loading users.

table

When using the Database authentication drivers, this option determines the database table containing the users of your application.

We are going to use the Eloquent authentication driver for this demo app.

Before we begin, you’re going to need to create a new table and a corresponding Eloquent data model to store our user details. We can name this table and data model whatever we like. To avoid unnecessary tinkering with theapp/config/auth.php, we will abide by the default Laravel convention:

Table nameusers
Model nameUser

Let’s begin by installing Laravel migrations using the Artisan command-line tool:

$ php artisan migrate:install
Nice! Now we're ready to do some migrating!

Now we will create our users migration table:

$ php artisan migrate:make create_users_table
Migration created successfully!

Let’s open up the new migration file in the app/database/migrations folder (mine is named2012_12_31_075518_create_users_table.php). Here’s how to create a suitable table with the Schema Builder:

public function up()
{
        Schema::create('users', function($t) {
                $t->increments('id');
                $t->string('username', 16);
                $t->string('password', 64);
                $t->timestamps();
        });
}

You may add as many additional columns as you wish, but for our demo app this is sufficient.

Here’s the rollback code for this migration:

public function down()
{
        Schema::drop('users');
}

Now, let’s apply the migration:

# php artisan migrate
Migrated: 2012_12_31_075518_create_users_table

Now we will create a sample user that we can use to test the authentication process. Create a new file namedusers.php in the app/database/seeds folder and copy-paste the following code:

<?php

return array(
        array(
                'username' => 'max',
                'password' => Hash::make('my_pass')
        ),
);

In the above snippet, we are populating the username and password columns of our users table. We are using the Laravel Hash class to generate a hash of the password using highly secure bcrypt algorithm. By storing the hash in the database instead of the plain text password, it offers our users some extra security. This is a very common practice with web applications.

Let’s populate our database with the sample data:

# php artisan db:seed
Seeded table: users (1 records)

Now we have a sample user in the system.

Laravel 4 ships with a pre-generated User model by default in (see app/models/User.php). For our demo app the default model will suffice.

Now, we may begin building the application. Let’s contemplate of the site structure in terms of url routes and HTTP verbs:

UrlRoute NameHTTP VerbPurpose
/homeGETShow home page
/loginloginGETShow login form
/loginN/APOSTLog in (authenticate) the user
/profileprofileGETProtected resource - only available to logged in users
/logoutlogoutGETLogs out the user

Routes

Let’s define the skeleton route handlers in app/routes.php as per our REST-ful schema:

Route::get('/', array('as' => 'home', function () { }));

Route::get('login', array('as' => 'login', function () { }));

Route::post('login', function () { });

Route::get('logout', array('as' => 'logout', function () { }));

Route::get('profile', array('as' => 'profile', function () { }));

On second thought, we don’t want an already logged in user to be able to login. We can prevent that by attaching abefore() filter to the login route:

Route::get('login', array('as' => 'login', function () { }))->before('guest');

Let’s open up the app/filters.php file and implement the “guest” filter. Laravel comes with a default “guest” filter, so we’ll only need to slightly modify the code:

Route::filter('guest', function()
{
        if (Auth::check()) 
                return Redirect::route('home')
                        ->with('flash_notice', 'You are already logged in!');
});

Here we use the Auth::check() method to test if the user is authenticated, and redirect the logged in user back to the home route based on that test.

Similarly the logout and profile routes should apply only to authenticated users. We need to protect these route from non-authenticated users. We can attach before() filters to these routes:

Route::get('logout', array('as' => 'logout', function () { }))->before('auth');

Route::get('profile', array('as' => 'profile', function () { }))->before('auth');

The corresponding “auth” filter in the app/filters.php file looks like this:

Route::filter('auth', function()
{
        if (Auth::guest())
                return Redirect::route('login')
                        ->with('flash_error', 'You must be logged in to view this page!');
});

We use the Auth::guest() method to guard access unless a user is currently ‘logged in’ using Laravels authentication system. Auth::guest() returns true only if the current request has no logged in user. As you can see, if no user is logged in, the auth filter returns a redirect to the login route, overriding the view supplied by our route.

Now, let’s begin implementing the routes with closures. We’ll start with the “home” route:

Route::get('/', array('as' => 'home', function () {
    return View::make('home');
}));

The “home” route simply loads the home.blade.php template from the app/views folder and returns the generated content.

We will create a simple CSS stylesheet for our webpages in public/css/style.css. Please refer to the laravel_auth repository for the content of the stylesheet.

We should strive to abide by good programming practice. We’ll create a master layout file that will serve as the parent template for all our sub-templates.

Let’s create the file layout.blade.php in the app/views folder:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<head>
    <title>Laravel Authentication Demo</title>
    {{ HTML::style('/css/style.css') }}
</head>
<body>
    <div id="container">
        <div id="nav">
            <ul>
                <li>{{ HTML::route('home', 'Home') }}</li>
                @if(Auth::check())
                    <li>{{ HTML::route('profile', 'Profile' ) }}</li>
                    <li>{{ HTML::route('logout', 'Logout ('.Auth::user()->username.')') }}</li>
                @else
                    <li>{{ HTML::route('login', 'Login') }}</li>
                @endif
            </ul>
        </div><!-- end nav -->

        <!-- check for flash notification message -->
        @if(Session::has('flash_notice'))
            <div id="flash_notice">{{ Session::get('flash_notice') }}</div>
        @endif

        @yield('content')
    </div><!-- end container -->
</body>
</html>

We use the Meido HTML helper library to programmatically generate the template. Most of the template is self-explanatory. I would draw your attention to the “nav” section. Here we dynamically construct the navigation links based on the authentication status of the user:

@if(Auth::check())
    <li>{{ HTML::route('profile', 'Profile' ) }}</li>
    <li>{{ HTML::route('logout', 'Logout (' . Auth::user()->username . ')') }}</li>
@else
    <li>{{ HTML::route('login', 'Login') }}</li>
@endif

We use the Auth::check() method to check if the user is authenticated, and generate separate set of navigation links based on this test.

Here’s the home.blade.php partial view template:

@extends('layout')

@section('content')
    <h1>Home page</h1>
    <p>Current time: {{ date('F j, Y, g:i A') }}  </p>
@stop

Nothing fancy here.

Handling Login

Let’s now implement the GET /login route:

Route::get('login', array('as' => 'login', function () {
    return View::make('login');
}))->before('guest');

The login route simply displays the app/views/login.blade.php template.

Time to create the login form. Here’s the code to create the login form:

@extends('layout')

@section('content')
    <h1>Login</h1>

    <!-- check for login error flash var -->
    @if (Session::has('flash_error'))
        <div id="flash_error">{{ Session::get('flash_error') }}</div>
    @endif

    {{ Form::open('login', 'POST') }}

    <!-- username field -->
    <p>
        {{ Form::label('username', 'Username') }}<br/>
        {{ Form::text('username', Input::old('username')) }}
    </p>

    <!-- password field -->
    <p>
        {{ Form::label('password', 'Password') }}<br/>
        {{ Form::password('password') }}
    </p>

    <!-- submit button -->
    <p>{{ Form::submit('Login') }}</p>

    {{ Form::close() }}
@stop

We use the Form helper class from the Meido Form library. We’ll explain a few things:

{{ Form::open('login', 'POST') }}

Here we specify that the form data should be submitted to the Route::post('login') route.

Rest of the form is fairly self-explanatory. Please refer to the official Meido Form documentation.

Now, let’s implement the POST /login route. This route will handle the actual user authentication:

Route::post('login', function () {
        $user = array(
            'username' => Input::get('username'),
            'password' => Input::get('password')
        );
        
        if (Auth::attempt($user)) {
            return Redirect::route('home')
                ->with('flash_notice', 'You are successfully logged in.');
        }
        
        // authentication failure! lets go back to the login page
        return Redirect::route('login')
            ->with('flash_error', 'Your username/password combination was incorrect.')
            ->withInput();
});

First let’s get the input data that has been posted from the form:

$user = array(
  'username' => Input::get('username'),
  'password' => Input::get('password')
);

Logging a user into your application is simple. We will use the Auth::attempt() method to verify the login credentials. The great thing about this method, is on success it will automatically create our authenticated session for us!

if (Auth::attempt($user)) {
  return Redirect::route('home')
      ->with('flash_notice', 'You are successfully logged in.');
}

We pass an array containing the username and password of the user to the Auth::attempt() method. If the user’s credentials are valid, the user ID will be stored in the session and the user will be considered “logged in” on subsequent requests to your application. The Auth::attempt() method will return true if the credentials are valid. Otherwise, false will be returned.

Now if our authentication is successful, the login session is created, and we are redirected to the home route.

Perfect! But before we go any further, let’s implement the logout route so that we can logout to perform future testing.

Route::get('logout', array('as' => 'logout', function () {
    Auth::logout();

    return Redirect::route('home')
        ->with('flash_notice', 'You are successfully logged out.');
}))->before('auth');

Here we use the Auth::logout() method to destroy the users login session, and head back to the home page. We are now logged out.

Now that we have our login/logout working perfectly let’s implement our super-secret private route:

Route::get('profile', array('as' => 'profile', function () {
    return View::make('profile');
}))->before('auth');

And below is the content of the corresponding app/views/profile.blade.php template:

@extends('layout')

@section('content')
  <h2>Welcome "{{ Auth::user()->username }}" to the protected page!</h2>
  <p>Your user ID is: {{ Auth::user()->id }}</p>
@stop

You will notice that we use Auth::user() in the above example. This method will return the currently logged in user object. We can access all the attributes of our User model using this method.

Here are some screenshots of the demo application we have built.

The first one is the public home page:

Now the login form:

Login error page:

Login success page:

Prevent duplicate authentication (for logged in users):

The protected profile page:

After logging out:

And finally, access denied when you try to visit http://hostname/profile:

Logging In

You may use the Auth::login() method to login a user without checking their credentials, such as after a user first registers to use your application. Let’s see this method in action:

$username =  strtolower(Input::get('username'));
User::create([
        'username' => $username,
        'password' => Hash::make(Input::get('password')),
        ]);

$user = User::where('username', '=', $username)->first();

Auth::login($user);

In the above snippet we create a new user object, and subsequently log the user into our application.

普通分类: