14
12

More than 5 years have passed since last update.

Laravel5.2でトークン認証をカスタマイズしてみる

Last updated at Posted at 2016-08-29

Laravel5.2でトークン認証middlewareの先を拡張してみたのでメモ。

環境

PHP 5.6.23 (cli) (built: Jun 22 2016 08:56:52)
Laravel 5.2.43

やりたいこと

APIの、トークン認証を実装したい。

  1. トークンのキー名を変えたい(&他判定処理追加)。
  2. 検索テーブルの条件の複雑化

※認証であって、ログインではないので注意!
API認証をいかに安全に実装するか?論はかいてませんのであしからず。

middlewareでの認証の流れは、まとめてくださってる方がいらっしゃるのでそちらをご参考に。
(自分もソース読んだけどね・・・)
Laravelのmiddleware authを理解したい

他、公式

実装方法

トークン認証を拡張したかったので、デフォルトのクラスをextendsしていく方向で。
実際の認証として、
Illuminate\GuardHelpers#guest()
で呼ばれた先を変更してきます。

middleware

最初から用意されているものをそのまま。返却値は適宜。

\App\Http\Middleware\Authenticate.php
<?php

namespace Myapp\Http\Middleware;

use Closure;
use Illuminate\Support\Facades\Auth;

class Authenticate
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @param  string|null  $guard
     * @return mixed
     */
    public function handle($request, Closure $next, $guard = null)
    {

        if (Auth::guard($guard)->guest()) {
            // 独自になにがしか返却でもー
            return response('Unauthorized.', 401);
        }

        return $next($request);
    }
}

Auth::guard($guard)->guest() で、
上記の、Illuminate\GuardHelpers#guest()
が呼ばれてるので具体的なソースはそこを見ましょう。

独自のGuardクラスを実装

トークン認証を拡張したかったので、Illuminate\Auth\TokenGuardをextendして独自クラス作成。
Illuminate\Auth\TokenGuard#user() をオーバーライド。

MyTokenGuard.php
<?php

namespace Myapp\Services\Auth;


use Illuminate\Auth\TokenGuard;
use Illuminate\Contracts\Auth\UserProvider;
use Illuminate\Http\Request;

class MyTokenGuard extends TokenGuard {

    /**
     * Create a new authentication guard.
     *
     * @param  \Illuminate\Contracts\Auth\UserProvider  $provider
     * @param  \Illuminate\Http\Request  $request
     * @return void
     */
    public function __construct(UserProvider $provider, Request $request)
    {
        $this->request = $request;
        $this->provider = $provider;
        $this->inputKey = 'atoken';
        $this->storageKey = 'api_token';
    }

    /**
     * Get the token for the current request.
     *
     * @return string
     */
    protected function getTokenForRequest()
    {
        $token = $this->request->input($this->inputKey);

        if (empty($token)) {
            $token = $this->request->bearerToken();
        }

        // 独自に他のトークンをとって突きあわせ処理

        return $token; //DBと突き合わせるトークン
    }
}

これで、リクエストパラメータ"atoken"を取得するようになる。もちろん、ヘッダーの別のキーも取れる。

トークン用カラムを追加。

トークンを保存しておきたいテーブルにカラム追加しときましょう。

Schema::connection('mycon')->table('t_user', function (Blueprint $table) {
    $table->string('api_token', 60)->unique();
});

この時のカラム名=GuardクラスのstorageKeyの名前に。

独自のProviderクラスを実装

Eloquantを利用していてDBデータと突きあわせたかったので、Illuminate\Auth\EloquentUserProviderをextendsして独自クラスを作成。

MyTokenProvider.php
<?php

namespace Myapp\Services\Auth;

use Illuminate\Auth\EloquentUserProvider;

class MyTokenProvider extends EloquentUserProvider  {


    /**
     * Retrieve a user by the given credentials.
     *
     * @param  array  $credentials
     * @return \Illuminate\Contracts\Auth\Authenticatable|null
     */
    public function retrieveByCredentials(array $credentials)
    {
        if (empty($credentials)) {
            return;
        }

        // First we will add each credential element to the query as a where clause.
        // Then we can execute the query and, if we found a user, return it in a
        // Eloquent User "model" that will be utilized by the Guard instances.
        $query = $this->createModel()->newQuery();

        // 独自の条件をつける

        return $query->first();
    }
}

これで、MyTokenGuard#getTokenForRequest() で返却した値をキーにしてさらに条件追加して検索できますぜ。

AuthServiceProvider

Guardクラス、Providerクラスを登録します。

App\ProvidersAuthServiceProvider.php

<?php

namespace Myapp\Providers;

use Myapp\Services\Auth\MyTokenProvider;
use Myapp\Services\Auth\MyTokenGuard;
use Illuminate\Contracts\Auth\Access\Gate as GateContract;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
use Auth;

class AuthServiceProvider extends ServiceProvider
{
    /**
     * The policy mappings for the application.
     *
     * @var array
     */
    protected $policies = [
        'Myapp\Model' => 'Myapp\Policies\ModelPolicy',
    ];

    /**
     * Register any application authentication / authorization services.
     *
     * @param  \Illuminate\Contracts\Auth\Access\Gate  $gate
     * @return void
     */
    public function boot(GateContract $gate)
    {
        $this->registerPolicies($gate);

        $this->app['auth']->extend('my_token', function($app, $name, array $config) {
            return new MyTokenGuard(Auth::createUserProvider($config['provider']), $app['request']);
        });

        $this->app['auth']->provider('my_api_token', function($app, array $config) {
            // Return an instance of Illuminate\Contracts\Auth\UserProvider...
            return new MyTokenProvider($app['hash'], $config['model']);
        });
    }
}

'my_token','my_api_token' = あとでconfigに設定するキーです

config

ServiceProviderで記述したキーを、設定します。
※passwordsはいじってないので割愛。

config\auth.php
<?php

return [

    'defaults' => [
        'guard' => 'api',
        'passwords' => 'users',
    ],

    'guards' => [
        'api' => [
            'driver' => 'my_token',
            'provider' => 'my_api',
        ],
    ],


    'providers' => [
        'my_api'  => [
            'driver' => 'my_api_token',
            'model' => Myapp\THoge::class
        ],
    ],


    'passwords' => [
      
    ],

];

とりあえずここまで。
あとはPHPUnitとかでテストするべし。

(ちなみに、Guardクラスで、use Illuminate\Http\Request;を書き忘れてしばらくハマったんだなあ。。。)

参考

http://qiita.com/washio12/items/59f5cde23b4205973c6b
https://readouble.com/laravel/5.2/ja/authentication.html#adding-custom-guards

14
12
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
14
12