はじめに
前回の記事の続きです。今回はlaravel-doctrineを使いつつ、Authファサードで認証を行うところまで行きます。
laravel-doctrineで認証
Userエンティティの更新
まず、Laravelで認証できるようにUserエンティティを更新します。
あるクラスをLaravelで認証できるようにするためには、そのクラスにIlluminate\Contracts\Auth\Authenticatable
を実装する必要があります。また、そのクラスをデータベースから取得してくるIlluminate\Contracts\Auth\UserProvider
クラスを作る必要があります。
詳しくはこちらのドキュメントをご覧ください。
laravel-doctrineではIlluminate\Contracts\Auth\Authenticatable
の実装に必要なメソッドを既に定義したtrait
と、そのプロバイダーが用意されているのでこれらを使います。
また、ユーザーを一意に識別するカラムとしてemail
を追加しています。
<?php
namespace App\Entities;
use Doctrine\ORM\Mapping as ORM;
use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Contracts\Support\Arrayable;
use LaravelDoctrine\ORM\Auth\Authenticatable as AuthenticatableTrait;
/**
* @ORM\Entity
*/
class User implements Arrayable, Authenticatable
{
use AuthenticatableTrait;
/**
* @ORM\Id
* @ORM\Column(type="integer")
* @ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* @ORM\Column(type="string")
*/
protected $name;
/**
* @ORM\Column(type="string")
*/
protected $email;
public function getName()
{
return $this->name;
}
public function setName($name): self
{
$this->name = $name;
return $this;
}
public function getEmail()
{
return $this->email;
}
public function setEmail($email): self
{
$this->email = $email;
return $this;
}
public function toArray()
{
return [
'id' => $this->id,
'name' => $this->name,
];
}
}
エンティティを更新したら、マイグレーションの作成と適用をします。
sail artisan doctrine:migrations:diff
sail artisan doctrine:migrations:migrate
configの更新
laravel-doctrineが提供しているUseProviderを使うように設定ファイルを更新します。
...
'providers' => [
'users' => [
'driver' => 'doctrine', // laravel-doctrineが提供しているUserProvider
'model' => \App\Entities\User::class, // 変更
],
...
コントローラーの作成とルートの登録
コントローラーを作成し、login()
、register()
、profile()
アクションを作成します。
また、今回は認証にCookieを使うので、api
ミドルウェアにCookieを使う設定を入れます。
protected $middlewareGroups = [
...
...
'api' => [
'throttle:api',
\Illuminate\Session\Middleware\StartSession::class, // 追加
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
];
...
...
<?php
use App\Http\Controllers\AuthController;
use Illuminate\Support\Facades\Route;
Route::post('login', [AuthController::class, 'login']);
Route::post('register', [AuthController::class, 'register']);
Route::get('profile', [AuthController::class, 'profile']);
<?php
namespace App\Http\Requests;
use Illuminate\Auth\Events\Lockout;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\RateLimiter;
use Illuminate\Support\Str;
use Illuminate\Validation\ValidationException;
class LoginRequest extends FormRequest
{
public function authorize()
{
return true;
}
public function rules()
{
return [
'email' => 'required|string|email',
'password' => 'required|string',
];
}
}
<?php
namespace App\Http\Requests;
use Illuminate\Auth\Events\Lockout;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\RateLimiter;
use Illuminate\Support\Str;
use Illuminate\Validation\ValidationException;
class RegisterRequest extends FormRequest
{
public function authorize()
{
return true;
}
public function rules()
{
return [
'name' => 'required|string',
'email' => 'required|string|email',
'password' => 'required|string',
];
}
}
login()
ではポストパラメータで受け取ったemail
とpassword
を用いて、認証を行います。
Illuminate\Contracts\Auth\Authenticatable
、Illuminate\Contracts\Auth\UserProvider
を使用していますので、LaravelのAuthファサードを使って認証を行うことができています。
register()
ではポストパラメータで受け取ったデータを使ってUserをDBに登録します。前の記事でやったのと同じようにDBへの永続化はEntityManager
を使って行います。パスワードは平文ではなく、Illuminate\Contracts\Hashing\Hasher
を使って暗号化してから保存する必要があります。
profile()
では認証済みのUserを取得して、データをレスポンスで返します。こちらもlogin()
と同様にAuthファサードを使うことができます。
<?php
namespace App\Http\Controllers;
use App\Entities\User;
use App\Http\Requests\LoginRequest;
use App\Http\Requests\RegisterRequest;
use Doctrine\ORM\EntityManagerInterface;
use Illuminate\Contracts\Hashing\Hasher;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Auth;
class AuthController extends Controller
{
public function login(LoginRequest $request): JsonResponse
{
if (!Auth::attempt($request->validated())) {
return response()->json([
'Invalid credential'
], 400);
}
return response()->json([]);
}
public function register(RegisterRequest $request, Hasher $hasher, EntityManagerInterface $entityManager): JsonResponse
{
$input = $request->validated();
$user = new User();
$user->setName($input['name']);
$user->setEmail($input['email']);
$user->setPassword($hasher->make($input['password']));
$entityManager->persist($user);
$entityManager->flush();
return response()->json($user);
}
public function profile(): JsonResponse
{
$user = Auth::user();
return response()->json($user);
}
}
curlで実際に叩くとこのような感じです。
# Userを登録
$ curl http://localhost/api/register -d "name=TARO&email=test@example.com&password=password"
{"id":1,"name":"TARO"}
# 登録したUserでログイン
$ curl -vvv http://localhost/api/login -d "email=test@example.com&password=password"
....
....
< Set-Cookie: laravel_session=vjJ4KExxhGZt0qjtlxcw6w4tL3lsGalm8687V8O3; expires=Thu, 17-Jun-2021 16:20:09 GMT; Max-Age=7200; path=/; httponly; samesite=lax
...
...
[]%
# Cookieなしで叩くとレスポンスデータは空ですが
$ curl http://localhost/api/profile
{}
# Cookieをつけると認証が通ります
$ curl http://localhost/api/profile -b laravel_session=vjJ4KExxhGZt0qjtlxcw6w4tL3lsGalm8687V8O3
{"id":1,"name":"TARO"}
終わりに
laravel-doctrineを使った認証ですが、想像していたよりだいぶ楽でした。UserProvider
とAuthenticatable
の実装が提供されているのが大きかったです。laravelでは色々なinterface
が定義されており、これらを実装するだけで手軽に連携できるのがいいですね
。改めてinterface
の重要さを感じました。