Laravel Application Development Cookbook
上QQ阅读APP看书,第一时间看更新

Making a CAPTCHA-style spam catcher

One way to combat "bots" that automatically fill in web forms is by using the CAPTCHA technique. This shows the user an image with some random letters; the user must fill in a text field with those letters. In this recipe, we will create a CAPTCHA image and verify that the user has entered it correctly.

Getting ready

We need a standard Laravel installation and make sure we have the GD2 library installed on our server, so we can create an image.

How to do it...

To complete this recipe, follow these steps:

  1. In our app directory, create a directory named libraries, and in our composer.json file, update it as follows:
    "autoload": {
        "classmap": [
            "app/commands",
            "app/controllers",
            "app/models",
            "app/database/migrations",
            "app/database/seeds",
            "app/tests/TestCase.php",
            "app/libraries"
        ]
    },
  2. In our app/libraries directory, create a file named Captcha.php to hold our simple Captcha class:
    <?php
    class Captcha {
        public function make() 
        {
            $string = Str::random(6, 'alpha');
            Session::put('my_captcha', $string);
            
            $width      = 100;
            $height     = 25;
            $image      = imagecreatetruecolor($width,$height);
            $text_color = imagecolorallocate($image, 130, 130,130);
            $bg_color   = imagecolorallocate($image, 190, 190,190);
            
            imagefilledrectangle($image, 0, 0, $width, $height,$bg_color);        
            imagestring($image, 5, 16, 4, $string,$text_color);
    
            ob_start();
            imagejpeg($image);
            $jpg = ob_get_clean();
            return "data:image/jpeg;base64,". base64_encode($jpg);
        }
    }
  3. In the root of our app, open the command-line interface to update the composer autoloader:
    php composer.phar dump-autoload
    
  4. Create a route in routes.php to hold the form with captcha:
    Route::get('captcha', function() 
    {
        $captcha = new Captcha;
        $cap = $captcha->make();
        return View::make('captcha')->with('cap', $cap);
    });
  5. Create our captcha view in the app/views directory with the name captcha.php:
    <h1>Laravel Captcha</h1>
    <?php
    if (Session::get('captcha_result')) {
        echo '<h2>' . Session::get('captcha_result') . '</h2>';
    }
    ?>
    <?php echo Form::open() ?>
    <?php echo Form::label('captcha', 'Type these letters:') ?>
    <br>
    <img src="<?php echo $cap ?>">
    <br>
    <?php echo Form::text('captcha') ?>
    <br>
    <?php echo Form::submit('Verify!') ?>
    <?php echo Form::close() ?>
  6. Create a route to compare the captcha value and the user input:
    Route::post('captcha', function() 
    {
        if (Session::get('my_captcha') !==Input::get('captcha')) {
            Session::flash('captcha_result', 'No Match.');
        } else {
            Session::flash('captcha_result', 'They Match!');
        }
        return Redirect::to('captcha');
    });

How it works...

We begin by updating our composer.json file to add our libraries directory to the autoloader. Now, we can add any classes or libraries we'd like into that directory, even if they're custom classes or possibly some legacy code.

To keep things simple, we create a simple Captcha class with a single make() method. In this method, we first create a random string using Laravel's Str:random(), which we tell to output a 6-character string of only letters. We then save that string to a session, so we can use it for validation later.

Using the string, we create a 100x25 pixel jpg image, with a gray background and darker gray text. Instead of saving the file to the server, we use the output buffer and save the image data to a variable. That way, we can create a data URI to send back to our route.

Next, we need to run composer's dump-autoload command, so our new class can be used by the application.

In our captcha route, we use the Captcha class to create the captcha data URI and send it to our form. For our purposes, the form will simply display the image and ask for the characters in a text field.

When the user submits the form, we compare the Session that the Captcha class created with the user input. In this recipe, we're just checking if the two values match but we could also create a custom validation method and add it our rules. We then set a session saying if it matched or not, and return the user back to the CAPTCHA page.