Edited at

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

More than 3 years have passed since last update.

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