【Laravel】管理者認証APIを追加する

Laravelではデフォルトでユーザ認証機能が用意されていますが、管理者認証は作成する必要があります。
今回はAPIから利用する方法を紹介します。

ガードとプロバイダの設定

config/auth.phpを以下に設定します。

    'guards' => [
        'user' => [
            'driver' => 'session',
            'provider' => 'users',
        ],
        'admin' => [
            'driver' => 'session',
            'provider' => 'admins',
        ],
    ],

    // 今回はModes以下にModel配置してます、好きな階層で問題ありません
    'providers' => [
        'users' => [
            'driver' => 'eloquent',
            'model' => App\Models\User::class,
        ],
        'admins' => [
            'driver' => 'eloquent',
            'model' => App\Models\Admin::class,
        ],
    ],

    'passwords' => [
        'users' => [
            'provider' => 'users',
            'table' => 'password_resets',
            'expire' => 60,
        ],
        'admins' => [
            'provider' => 'admins',
            'table' => 'password_resets',
            'expire' => 60,
        ],
    ],

ガードとプロバイダについて詳しく知りたい方は、公式サイトを参照してください。

認証 5.5 Laravel

ミドルウェアの設定

今回はAPIから利用したいので、apiのミドルウェアグループを以下のようにします。
デフォルトのapiミドルウェアグループはトークンによる認証を想定しているので、クッキーやセッションを使いません。
今回はセッション方式で管理者のログインを行うので以下のような設定になります。

    /**
     * The application's route middleware groups.
     *
     * @var array
     */
    protected $middlewareGroups = [
        // ...web省略

        'api' => [
            'throttle:60,1',
            'bindings',
            // 追加
            \App\Http\Middleware\EncryptCookies::class,
            \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
            \Illuminate\Session\Middleware\StartSession::class,
            //\App\Http\Middleware\VerifyCsrfToken::class, // 本番では必須だが、簡略化のため省略する
        ],
    ];

モデル、マイグレーション、ファクトリー、シーダー、コントローラを生成

モデル作成と同時にマイグレーションとファクトリーを作成。
シーダーを作成。
コントローラーを作成。

php artisan make:model Models\\Admin -m -f
php artisan make:seeder AdminTableSeeder
php artisan make:controller Api\\AdminController

マイグレーション

<?php

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreateAdminsTable extends Migration
{
    public function up()
    {
        Schema::create('admins', function (Blueprint $table) {
            $table->increments('id');
            $table->string('name');
            $table->string('email')->unique();
            $table->string('password');
            $table->rememberToken();
            $table->timestamps();
        });
    }

    public function down()
    {
        Schema::dropIfExists('admins');
    }
}

シーダー記述

<?php

use Illuminate\Database\Seeder;
use App\Models\Admin;

class AdminTableSeeder extends Seeder
{
    public function run()
    {
        //削除
        Admin::truncate();

        $param = [
            'name' => "admin1",
            'email' => "admin1@admin.com",
            'password' => Hash::make('admin1'),
        ];
        DB::table('admins')->insert($param);
    }
}

シーダを呼び出す記述をDatabaseSeeder.phpに追加。

<?php

use Illuminate\Database\Seeder;

class DatabaseSeeder extends Seeder
{
    public function run()
    {
        $this->call(UserTableSeeder::class);
        $this->call(AdminTableSeeder::class);
    }
}

シーダー実行

composer dump-autoload
php artisan migrate:refresh --seed

モデルの記述追加

<?php

namespace App\Models;

use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;

class Admin extends Authenticatable
{
    use Notifiable;

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'name', 'email', 'password',
    ];

    /**
     * The attributes that should be hidden for arrays.
     *
     * @var array
     */
    protected $hidden = [
        'password', 'remember_token',
    ];

    /**
     * パスワードリセット通知の送信をオーバーライド
     *
     * @param  string  $token
     * @return void
     */
    public function sendPasswordResetNotification($token)
    {
        $this->notify(new PasswordResetNotification($token));
    }
}

コントローラーの処理追加

<?php

namespace App\Http\Controllers\Api;

use Illuminate\Http\Request;
use App\Http\Controllers\Controller;

class AdminController extends Controller
{

    public function login(Request $request)
    {
        $credentials = $request->only('email', 'password');

        if (!auth()->guard('admin')->attempt($credentials)) {
            return response()->json(['error' => 'invalid_credentials'], 401);
        }

        return response()->json(['message' => 'Successfully logged in.']);
    }

    public function me()
    {
        return response()->json(auth()->guard('admin')->user());
    }

    public function logout()
    {
        auth()->guard('admin')->logout();

        return response()->json(['message' => 'Successfully logged out.']);
    }
}

ルーティングの設定

ログイン、ログアウト、自身の情報を取得するルーティングを追加。

routes/api.php

Route::post('/admin/login',  'Api\AdminController@login');

Route::group(['middleware' => ['auth:admin']], function () {

    Route::get('/admin/me',  'Api\AdminController@me');

    Route::delete('/admin/logout',  'Api\AdminController@logout');

});

ここまでで管理者のログイン、ログアウト、管理者情報の取得までできました。
curlやPostmanでログインができるか確認してみてください。

[POST] /api/admin/login
[GET] /api/admin/me
[DELETE] /api/admin/logout

次に念のため簡単なテストを追加します。

テスト

正しく認証が行えているか確認のため、テストを追加します。

ファクトリーの追加

テストで使うユーザの作成。

<?php

use Faker\Generator as Faker;
use App\Models\Admin;

$factory->define(Admin::class, function (Faker $faker) {
    return [
        'name' => $faker->name,
        'email' => $faker->unique()->safeEmail,
        'password' => '$2y$10$TKh8H1.PfQx37YgCzwiKb.KjNyWgaHb9cbcoQgdIVFlYg7B77UdFm', // secret
        'remember_token' => str_random(10),
    ];
});

テストを記述

簡単なテストを追加。
テストを行うメソッドは日本語名でも大丈夫です。
何のテストかあとから見てもわかりやすいのでオススメです。

<?php

namespace Tests\Feature;

use Tests\TestCase;
use App\Models\Admin;

class AdminTest extends TestCase
{

    /**
     * 各テスト実行前に呼ばれる。
     */
    protected function setUp()
    {
        parent::setUp();
        $this->artisan('migrate');
        $this->seed('AdminTableSeeder');
    }

    public function test_api_auth_loginに正常パラメーターを付与しPOSTでアクセス可能()
    {
        $params = [
            'email' => 'admin1@admin.com',
            'password' => 'admin1',
        ];
        $response = $this->json('POST', '/api/admin/login', $params);
        $response->assertStatus(200);
    }

    public function test_api_admin_meに認証状態でアクセスできる()
    {
        $admin = factory(Admin::class)->create();

        $response = $this->actingAs($admin, 'admin')->json('GET','/api/admin/me');
        $response->assertStatus(200);
    }

    public function test_api_admin_logoutに認証状態でアクセスできる()
    {
        $admin = factory(Admin::class)->create();

        $response = $this->actingAs($admin, 'admin')->json('DELETE','/api/admin/logout');
        $response->assertStatus(200);
    }

    public function test_未認証状態では404()
    {
        $response = $this->json('GET','/api/admin/me');
        $response->assertStatus(401);

        $response = $this->json('DELETE','/api/admin/logout');
        $response->assertStatus(401);
    }

}

以上です。

コメント