【ララはじ #4】 Laravel9でも、ありがちなログイン認証を再現してみます。
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"}%
いけてそう…
ふぅ。
次は、フロントエンドからのアクセスですか…