Laravel5.2でトークン認証middlewareの先を拡張してみたのでメモ。
環境
PHP 5.6.23 (cli) (built: Jun 22 2016 08:56:52)
Laravel 5.2.43
やりたいこと
APIの、トークン認証を実装したい。
- トークンのキー名を変えたい(&他判定処理追加)。
- 検索テーブルの条件の複雑化
※認証であって、ログインではないので注意!
※API認証をいかに安全に実装するか?論はかいてませんのであしからず。
middlewareでの認証の流れは、まとめてくださってる方がいらっしゃるのでそちらをご参考に。
(自分もソース読んだけどね・・・)
Laravelのmiddleware authを理解したい
他、公式
実装方法
トークン認証を拡張したかったので、デフォルトのクラスをextendsしていく方向で。
実際の認証として、
Illuminate\GuardHelpers#guest()
で呼ばれた先を変更してきます。
middleware
最初から用意されているものをそのまま。返却値は適宜。
<?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() をオーバーライド。
<?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して独自クラスを作成。
<?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クラスを登録します。
<?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はいじってないので割愛。
<?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