Use DynamoDB as a Laravel session storage.

Thu, 06 Dec 2018 14:37:39


Introduction

Hello everyone. This time, I decided to construction Laravel application with ECS.

Session management becomes a problem as WEB applications including authentication operate in multiple containers. I discussed with an application representative and, as a result, I decided to use DynamoDB.

However, since Laravel does not have a mechanism to use DynamoDB as session storage by default, we decided to implement it inside the team this time.


Try

I'll construction while looking at the official documentation of Laravel. https://readouble.com/laravel/5.7/en/session.html


Implementing the driver

Your custom session driver should implement the SessionHandlerInterface. This interface contains just a few simple methods we need to implement. A stubbed MongoDB implementation looks something like this:

<?php

namespace App\Extensions;

class MongoSessionHandler implements \SessionHandlerInterface
{
    public function open($savePath, $sessionName) {}
    public function close() {}
    public function read($sessionId) {}
    public function write($sessionId, $data) {}
    public function destroy($sessionId) {}
    public function gc($lifetime) {}
}

It seems necessary to implement SessionHandlerInterface in order to use my session driver. By saying, I will create DynamoSessionHandler.php.

<?php
namespace App\Extensions;

class DynamoSessionHandler implements  implements \SessionHandlerInterface
{
    public function open($savePath, $sessionName) {}
    public function close() {}
    public function read($sessionId) {}
    public function write($sessionId, $data) {}
    public function destroy($sessionId) {}
    public function gc($lifetime) {}
}

Add an implementation while looking at the documentation of DynamoDB SessionHandler

https://docs.aws.amazon.com/aws-sdk-php/v3/api/class-Aws.DynamoDb.SessionHandler.html

It seems that DynamoDbClient and SessionHandler are necessary.

<?php

use Aws\DynamoDb\DynamoDbClient;
use Aws\DynamoDb\SessionHandler;

Also initializes the DynamoDbSessionHandler when initializing the SessionHandler.

<?php

class DynamoSessionHandler implements  implements \SessionHandlerInterface
{
    protected $client;
    protected $handler;

    public function __construct(DynamoDbClient $client, array $config)
    {
        $this->client = $client;
        $this->handler = \Aws\DynamoDb\SessionHandler::fromClient($client, $config);
    }

    ...

}

Implementation for actually using DynamoDB as a session handler, such as open (), read (), is implemented by SessionHandler of AWS SDK. So, I can pass input to SDK as it is.

<?php

class DynamoSessionHandler implements  implements \SessionHandlerInterface
{

    ...

    public function open($save_path, $session_id)
    {
        return $this->handler->open($save_path, $session_id);
    }

   ...
}

Registering the driver

I will review the official document of Laravel again.

Once your driver has been implemented, you are ready to register it with the framework. To add additional drivers to Laravel's session backend, you may use the extend method on the Session facade. You should call the extend method from the boot method of a service provider. You may do this from the existing AppServiceProvider or create an entirely new provider:

<?php

namespace App\Providers;

use App\Extensions\MongoSessionHandler;
use Illuminate\Support\Facades\Session;
use Illuminate\Support\ServiceProvider;

class SessionServiceProvider extends ServiceProvider
{
    /**
     * Perform post-registration booting of services.
     *
     * @return void
     */
    public function boot()
    {
        Session::extend('mongo', function ($app) {
            // Return implementation of SessionHandlerInterface...
            return new MongoSessionHandler;
        });
    }

    /**
     * Register bindings in the container.
     *
     * @return void
     */
    public function register()
    {
        //
    }
}

It seems necessary to let Laravel recognize the driver. I register driver according to the official document.

First, create DynamoSessionServiceProvider.php.

<?php

namespace App\Providers;

use App\Extensions\DynamoSessionHandler;
use Aws\DynamoDb\DynamoDbClient;
use Illuminate\Support\Facades\Session;
use Illuminate\Support\ServiceProvider;

class DynamoSessionServiceProvider extends ServiceProvider
{
    const DEFAULT_REGION = 'ap-northeast-1';

    public function boot()
    {
        Session::extend('dynamo', function ($app) {
            $cfg = $app['config']->get('session');

            $dynamoDbClient = DynamoDbClient::factory([
                'region' => (isset($cfg['region']) ? $cfg['region'] : self::DEFAULT_REGION),
                "endpoint" => (isset($cfg['endpoint']) ? $cfg['endpoint'] : 'https://dynamodb.'.self::DEFAULT_REGION.'.amazonaws.com'),
                'version'  => 'latest',
            ]);

            $config = [
                'table_name'               => $cfg['table'],
                'hash_key'                 => 'id',
                'session_lifetime'         => 60 * $cfg['lifetime'],
                'consistent_read'          => true,
                'locking_strategy'         => null,
                'automatic_gc'             => true,
                'gc_batch_size'            => 25,
                'max_lock_wait_time'       => 10,
                'min_lock_retry_microtime' => 10000,
                'max_lock_retry_microtime' => 50000
            ];
            // Returns the implementation of SessionHandlerInterface...
            return new DynamoSessionHandler($dynamoDbClient, $config);
        });
    }

    // Since we are directly implementing it this time, we do not use register ()
    public function register() {}
}

When creating DynamoDbClient, I can specify regions and endpoints from environment variables. I can now use DynamoSessionHandler with the Laravel application.


Use as session storage

Ready to use DynamoDB as session storage.

Let's say session-table for DynamoDB used for session management. Create session-table on the AWS environment. Since the hash key is set to id on the code, it creates it so as not to make a mistake.

$ aws dynamodb create-table \n    --table-name session-table \n    --attribute-definitions '[{"AttributeName":"id","AttributeType": "S"}]' \n    --key-schema '[{"AttributeName":"id","KeyType": "HASH"}]' \n    --provisioned-throughput '{"ReadCapacityUnits": 5,"WriteCapacityUnits": 5}'

Lastly, I include the necessary information in config/session.php.

<?php
return [

    /*
    |--------------------------------------------------------------------------
    | Default Session Driver
    |--------------------------------------------------------------------------
    |
    | This option controls the default session "driver" that will be used on
    | requests. By default, we will use the lightweight native driver but
    | you may specify any of the other wonderful drivers provided here.
    |
    | Supported: "file", "cookie", "database", "apc",
    |            "memcached", "redis", "array"
    |
    */

    'driver' => 'dynamo',
    'region' => 'ap-northeast-1',
    'table' => 'session-table',

    ...

Successfully to use DynamoDB as session storage in Laravel. (There is no change on the screen)