見出し画像

【ララはじ #4】 Laravel9でも、ありがちなログイン認証を再現してみます。

ちょっと、Web系の開発から離れていたので、今時どんな感じになってるのかを確認する為にいろいろやってみた備忘録です。

築山五郎の人生語録より

Vueもなんとなく動いたので(ちょっと不安w)、バックエンド側のAPIサーバーをLaravelでつくっていくという、ありがちな構成(以前作っていたような記憶…)を再現します。

よく見ると、SanctumもCORSも標準で入ってました。
この辺りを使える様に設定変更(内容確認?)していきます。

まず、Sanctumですが、Route Middleware Groupsのところが有効化されてない(コメントアウトされている)ので、これを有効化します。とりあえず、セッションも有効化します。
app/Http/Kernel.php ファイルを修正します。

    protected $middlewareGroups = [
        'web' => [
            \App\Http\Middleware\EncryptCookies::class,
            \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
            \Illuminate\Session\Middleware\StartSession::class,
            \Illuminate\View\Middleware\ShareErrorsFromSession::class,
            \App\Http\Middleware\VerifyCsrfToken::class,
            \Illuminate\Routing\Middleware\SubstituteBindings::class,
        ],

        'api' => [
            \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,  // <-- ここ
            'throttle:api',
            \Illuminate\Routing\Middleware\SubstituteBindings::class,
        ],
    ];

CORSに関して、今回関係ないような気がしますが、
とりあえず、クロスオリジンのリクエストでCookieを付与するように設定します。サーバー側ではレスポンスヘッダーにAccess-Control-Allow-Credentials: trueを付与するために、config/cors.php ファイルを修正します。

    'paths' => ['api/*', 'sanctum/csrf-cookie'],

    'allowed_methods' => ['*'],

    'allowed_origins' => ['*'],

    'allowed_origins_patterns' => [],

    'allowed_headers' => ['*'],

    'exposed_headers' => [],

    'max_age' => 0,

    'supports_credentials' => true,     // <-- ここ

];

まぁ、allowed_origins が誰でもアクセスできます設定になってるので、適宜修正します。

さて、認証するわけなので、仮で認証用のアカウントを作成するために、Seederを使って、適当にアカウント作っていきます。

% sail artisan make:seeder UserSeeder

database/seeders/UserSeeder.php というファイルが作成されるので、編集していきます。

<?php

namespace Database\Seeders;

use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;      // <-- ここ
use Illuminate\Support\Facades\Hash;    // <-- ここ

class UserSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        // ↓ ここから
        DB::table('users')->insert([
            [
                'name' => 'user',
                'email' => 'user@goro-tsukiyama.com',
                'password' => Hash::make('0Tame4'),
            ],
            [
                'name' => 'guest',
                'email' => 'guest@goro-tsukiyama.com',
                'password' => Hash::make('0Tame4'),
            ],
            [
                'name' => 'super',
                'email' => 'super@goro-tsukiyama.com',
                'password' => Hash::make('0Tame4'),
            ],
        ]);
        // ↑ ここまで
    }
}

artisanコマンドで一発導入するために、database/seeders/DatabaseSeeder.php ファイルを修正します。

class DatabaseSeeder extends Seeder
{
    /**
     * Seed the application's database.
     *
     * @return void
     */
    public function run()
    {
        // ↓ ここから
        $this->call([
            \Database\Seeders\UserSeeder::class,
        ]);
        // ↑ ここまで
    }
}

artisanコマンドでアカウントデータを投入します。

% sail artisan migrate:fresh --seed

ただ、このコマンドは、すべてのテーブルが削除され、すべてのマイグレーションが再実行されるので、注意は必要ですw

データが投入されたのか、一応、コマンドラインツールを起動して確認してみます。

% sail artisan tinker
Psy Shell v0.11.9 (PHP 8.1.12 — cli) by Justin Hileman
> App\Models\User::all();
= Illuminate\Database\Eloquent\Collection {#4420
    all: [
      App\Models\User {#4674
        id: 1,
        name: "user",
        email: "user@goro-tsukiyama.com",
        email_verified_at: null,
        #password: "$2y$10$tbjhKFdfRXA9bTL1E/fUP.rawfO5O8JciLOytHnmYsrVwkfg19Fjy",
        #remember_token: null,
        created_at: null,
        updated_at: null,
      },
      App\Models\User {#4675
        id: 2,
        name: "guest",
        email: "guest@goro-tsukiyama.com",
        email_verified_at: null,
        #password: "$2y$10$rYAq/h9791OOOdYNCQnE0O8izm2/TWJqJZVBdRzi999ZltKToZVxm",
        #remember_token: null,
        created_at: null,
        updated_at: null,
      },
      App\Models\User {#4676
        id: 3,
        name: "super",
        email: "super@goro-tsukiyama.com",
        email_verified_at: null,
        #password: "$2y$10$8GkDftZvqcu.EA051Ms/BuxctcHNURjLA.vhpElBYGcdZa8c9Cja6",
        #remember_token: null,
        created_at: null,
        updated_at: null,
      },
    ],
  }

> exit

   INFO  Goodbye.

入ってた(方法合ってたw

材料は揃ってきたので、ログインAPI用のコントローラーを作成します。
今回はLaravel側はバックエンド専用なので特にディレクトリ分けせずにそのまま作成します。

% sail artisan make:controller LoginController

app/Http/Controllers/LoginController.php が作成されるので編集していきます。

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;                // <-- ここ
use \Symfony\Component\HttpFoundation\Response;     // <-- ここ
use App\Models\User;                                // <-- ここ

class LoginController extends Controller
{
    // ↓ ここから
    /**
     * @param  Request  $request
     * @return \Illuminate\Http\JsonResponse
     */
    public function login(Request $request)
    {
        $credentials = $request->validate([
            'email' => ['required', 'email'],
            'password' => ['required'],
        ]);

        if (!Auth::attempt($credentials)) {
            return response()->json([], Response::HTTP_UNAUTHORIZED);
        }
        try {
            $user = User::whereEmail($request->email)->firstOrFail();
            $token = $user->createToken("login:user{$user->id}")->plainTextToken;
            return response()->json(['token' => $token], Response::HTTP_OK);
        } catch(ModelNotFoundException $e) {
            return response()->json([], Response::HTTP_INTERNAL_SERVER_ERROR);
        }
    }

    /**
     * @param  Request  $request
     * @return \Illuminate\Http\JsonResponse
     */
    public function logout(Request $request)
    {
        auth('sanctum')->user()->tokens()->delete();
        Auth::logout();
        return response()->json([], Response::HTTP_OK);
    }
    // ↑ ここまで
}

loginとlogoutのメソッドを用意してます。

次にRouteの設定をします。

まず、フロントエンド側ですが、routes/web.php も修正しておきます。
全部、Vueへ振ります。なので、Vue側でのルーティングが必要になります。(これは別のお話

<?php

use Illuminate\Support\Facades\Route;

/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/

Route::get('/{any}', function () {
    return view('welcome');
})->where('any', '.*');

そしてバックエンド側に、ログインとログアウトのルートを追記します。
routes/api.php を修正します。

<?php

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\LoginController;   // <-- ここ

/*
|--------------------------------------------------------------------------
| API Routes
|--------------------------------------------------------------------------
|
| Here is where you can register API routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| is assigned the "api" middleware group. Enjoy building your API!
|
*/

Route::middleware('auth:sanctum')->get('/user', function (Request $request) {
    return $request->user();
});
// ↓ ここから
Route::post('login', [LoginController::class, 'login']);
Route::post('logout', [LoginController::class, 'logout']);
// ↑ ここまで

ちなみに、prefixがapiなので、実際にアクセスするときは
/api/login
/api/logout
になります。

cURLコマンドで確認します

% curl -X POST localhost/api/login -d email=user@goro-tsukiyama.com -d password=0Tame4

{"token":"3|zssS85HFH9WBn6jWAwMc8pToc0BWsg0PwQIK7jWy"}%

いけてそう…

ふぅ。

次は、フロントエンドからのアクセスですか…

いいなと思ったら応援しよう!