「Laravelのユーザー認証機能簡単に実装できるけど、どういう処理が走っているのかよく分からない…」
今回の記事はこんな悩みに対して書きました。
本記事を書いているSHOOT(@Shoot58153748)は
未経験からWeb系に転職したWebエンジニア1年目で、
主にLaravelでアプリ開発しています。
会社では独自のユーザー認証方法を用いるものの、
Laravel標準のユーザー認証ってありますよね?
Laravelを使い始めた当初仕組みがさっぱりでした。
どういう処理が行われているのか知らないと気持ち悪いですよね??
そこで本記事では
標準ユーザー認証の処理の流れを解説します!
ぜひ参考にしてください!
目次
- 1 ユーザー認証(Register)のだいたいの流れ
- 2 ユーザー認証準備
- 3 登録処理詳細
- 3.0.1 app/Http/Auth/RegisterController.php
- 3.0.2 app/Http/Kernel.php
- 3.0.3 app/Http/Middleware/RedirectIfAuthenticated.php
- 3.0.4 /vendor/laravel/framework/src/Illuminate/foundation/Auth/RegistersUsers.php
- 3.0.5 /vendor/laravel/framework/src/Illuminate/events/Registered.php
- 3.0.6 /vendor/laravel/framework/src/Illuminate/support/facades/Auth.php
- 3.0.7 /vendor/laravel/framework/src/Illuminate/Auth/AuthManager.php
- 3.0.8 /vendor/laravel/framework/src/Illuminate/Auth/SessionGuard.php
- 4 ライブラリを読みこむのは勉強になる
- ユーザー認証(Register)のだいたいの流れ
- ユーザー認証準備
- 登録処理詳細
- app/Http/Auth/RegisterController.php
- app/Http/Kernel.php
- app/Http/Middleware/RedirectIfAuthenticated.php
- /vendor/laravel/framework/src/Illuminate/foundation/Auth/RegistersUsers.php
- /vendor/laravel/framework/src/Illuminate/events/Registered.php
- /vendor/laravel/framework/src/Illuminate/support/facades/Auth.php
- /vendor/laravel/framework/src/Illuminate/Auth/AuthManager.php
- /vendor/laravel/framework/src/Illuminate/Auth/SessionGuard.php
ユーザー認証(Register)のだいたいの流れ
登録画面を表示する
ユーザー登録画面からPOSTフォームでユーザー情報(name,email,password)を送信する
ユーザー情報をバリデーションチェックする
ユーザー情報をDBに保存する
ログイン(ユーザーIDをSession保存)する
Home画面にリダイレクトする
ユーザー認証準備
// ユーザー認証に必要なルーティングを追加する
php artisan make:auth
// ルーティング一覧を確認する
php artisan route:list
実際の登録処理(POST)は
RegisterControllerのregisterに
ルーティングされています。
登録処理詳細
app/Http/Auth/RegisterController.php
<?php
namespace App\Http\Controllers\Auth;
use App\User;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Validator;
use Illuminate\Foundation\Auth\RegistersUsers;
class RegisterController extends Controller
{
/*
|--------------------------------------------------------------------------
| Register Controller
|--------------------------------------------------------------------------
|
| This controller handles the registration of new users as well as their
| validation and creation. By default this controller uses a trait to
| provide this functionality without requiring any additional code.
|
*/
use RegistersUsers;
/**
* Where to redirect users after registration.
*
* @var string
*/
protected $redirectTo = '/home';
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('guest');
}
/**
* Get a validator for an incoming registration request.
*
* @param array $data
* @return \Illuminate\Contracts\Validation\Validator
*/
protected function validator(array $data)
{
return Validator::make($data, [
'name' => ['required', 'string', 'max:255'],
'email' => ['required', 'string', 'email', 'max:255', 'unique:users'],
'password' => ['required', 'string', 'min:8', 'confirmed'],
]);
}
/**
* Create a new user instance after a valid registration.
*
* @param array $data
* @return \App\User
*/
protected function create(array $data)
{
return User::create([
'name' => $data['name'],
'email' => $data['email'],
'password' => Hash::make($data['password']),
]);
}
}
処理の順番としては以下になります。
1. ミドルウェア(コンストラクター)
2. ハンドラー(ルーティングで指示されたメソッド)
コンストラクターはクラスが生成時に必ず実行させる処理です。
public function __construct()
{
$this->middleware('guest');
}
‘guest’の定義に関しては
app/Http/Kernel.phpに記述されています。
app/Http/Kernel.php
/**
* The application's route middleware.
*
* These middleware may be assigned to groups or used individually.
*
* @var array
*/
protected $routeMiddleware = [
'auth' => \App\Http\Middleware\Authenticate::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
'can' => \Illuminate\Auth\Middleware\Authorize::class,
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
];
‘guest’というエイリアスが登録されており、
\App\Http\Middleware\RedirectIfAuthenticated::classを実行。
app/Http/Middleware/RedirectIfAuthenticated.php
/**
* 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)->check()) {
return redirect('/home');
}
return $next($request);
}
handleメソッド内を実行します。
ログイン(ユーザー情報のセッションが残っていたら)していたら
ホーム画面にリダイレクト。
Auth::guard(ファサード)に関しては後述します。
今回は未ログインという前提で話を進めます。
回答画面からユーザーネーム、メールアドレス、パスワードを送信(POSTリクエスト)されると、
RegisterControllerのregisterメソッドにルーティングされます。
ですが、、、regisiterメソッドが見当たりません。
実は、use RegistersUsers;
の中に処理が隠れていました。
useは、宣言したクラスを使うことができます。
/vendor/laravel/framework/src/Illuminate/foundation/Auth/RegistersUsers.php
/**
* Show the application registration form.
*
* @return \Illuminate\Http\Response
*/
public function showRegistrationForm()
{
return view('auth.register');
}
/**
* Handle a registration request for the application.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function register(Request $request)
{
$this->validator($request->all())->validate();
event(new Registered($user = $this->create($request->all())));
$this->guard()->login($user);
return $this->registered($request, $user)
?: redirect($this->redirectPath());
}
/**
* Get the guard to be used during registration.
*
* @return \Illuminate\Contracts\Auth\StatefulGuard
*/
protected function guard()
{
return Auth::guard();
}
registerというメソッドがありました!
この中に、登録処理の全てが記述されています。
以下の流れです。
ユーザー情報をバリデートする(validotor)
↓↓
DBに登録(create)
↓↓
セッションに保存(guard()->login())
↓↓
リダイレクト(return)
validator, createメソッドはControllerに定義されてました。
$this->guard()は同クラス内に定義されており、
Auth::guard()というAuthファサードのguardメソッドを返しています。
ちなみに、以下のイベントクラスでDB登録したユーザーを設定します。
event(new Registered($user = $this->create($request->all())));
/vendor/laravel/framework/src/Illuminate/events/Registered.php
/**
* The authenticated user.
*
* @var \Illuminate\Contracts\Auth\Authenticatable
*/
public $user;
/**
* Create a new event instance.
*
* @param \Illuminate\Contracts\Auth\Authenticatable $user
* @return void
*/
public function __construct($user)
{
$this->user = $user;
}
}
さて、ログイン処理を行なっているAuthファサードクラスを見てみましょう
/vendor/laravel/framework/src/Illuminate/support/facades/Auth.php
/**
* @method static mixed guard(string|null $name = null)
* @method static void shouldUse(string $name);
* @method static bool check()
* @method static bool guest()
* @method static \Illuminate\Contracts\Auth\Authenticatable|null user()
* @method static int|null id()
* @method static bool validate(array $credentials = [])
* @method static void setUser(\Illuminate\Contracts\Auth\Authenticatable $user)
* @method static bool attempt(array $credentials = [], bool $remember = false)
* @method static bool once(array $credentials = [])
* @method static void login(\Illuminate\Contracts\Auth\Authenticatable $user, bool $remember = false)
* @method static \Illuminate\Contracts\Auth\Authenticatable loginUsingId(mixed $id, bool $remember = false)
* @method static bool onceUsingId(mixed $id)
* @method static bool viaRemember()
* @method static void logout()
* @method static \Symfony\Component\HttpFoundation\Response|null onceBasic(string $field = 'email',array $extraConditions = [])
* @method static null|bool logoutOtherDevices(string $password, string $attribute = 'password')
* @method static \Illuminate\Contracts\Auth\UserProvider|null createUserProvider(string $provider = null)
* @method static \Illuminate\Auth\AuthManager extend(string $driver, \Closure $callback)
* @method static \Illuminate\Auth\AuthManager provider(string $name, \Closure $callback)
*
* @see \Illuminate\Auth\AuthManager
* @see \Illuminate\Contracts\Auth\Factory
* @see \Illuminate\Contracts\Auth\Guard
* @see \Illuminate\Contracts\Auth\StatefulGuard
*/
class Auth extends Facade
{
/**
* Get the registered name of the component.
*
* @return string
*/
protected static function getFacadeAccessor()
{
return 'auth';
}
/**
* Register the typical authentication routes for an application.
*
* @param array $options
* @return void
*/
public static function routes(array $options = [])
{
static::$app->make('router')->auth($options);
}
}
「あれ、メソッドどこに書かれているんだ?」
と思うかもしれませんが、継承元のFacade.phpに
getFacadeAccessorで’auth’を返すことで複数のクラス
@see \Illuminate\Auth\AuthManager
@see \Illuminate\Contracts\Auth\Factory
@see \Illuminate\Contracts\Auth\Guard
@see \Illuminate\Contracts\Auth\StatefulGuard
を参照する仕組みになっています。
Facadeの処理に関しては別記事で詳しく流れを追いたいと思います。
以下が、Authファサードのguardメソッドです。
/vendor/laravel/framework/src/Illuminate/Auth/AuthManager.php
/**
* Attempt to get the guard from the local cache.
*
* @param string|null $name
* @return \Illuminate\Contracts\Auth\Guard|\Illuminate\Contracts\Auth\StatefulGuard
*/
public function guard($name = null)
{
$name = $name ?: $this->getDefaultDriver();
return $this->guards[$name] ?? $this->guards[$name] = $this->resolve($name);
}
/**
* Resolve the given guard.
*
* @param string $name
* @return \Illuminate\Contracts\Auth\Guard|\Illuminate\Contracts\Auth\StatefulGuard
*
* @throws \InvalidArgumentException
*/
protected function resolve($name)
{
$config = $this->getConfig($name);
if (is_null($config)) {
throw new InvalidArgumentException("Auth guard [{$name}] is not defined.");
}
if (isset($this->customCreators[$config['driver']])) {
return $this->callCustomCreator($name, $config);
}
$driverMethod = 'create'.ucfirst($config['driver']).'Driver';
if (method_exists($this, $driverMethod)) {
return $this->{$driverMethod}($name, $config);
}
throw new InvalidArgumentException(
"Auth driver [{$config['driver']}] for guard [{$name}] is not defined."
);
}
/**
* Create a session based authentication guard.
*
* @param string $name
* @param array $config
* @return \Illuminate\Auth\SessionGuard
*/
public function createSessionDriver($name, $config)
{
$provider = $this->createUserProvider($config['provider'] ?? null);
$guard = new SessionGuard($name, $provider, $this->app['session.store']);
// When using the remember me functionality of the authentication services we
// will need to be set the encryption instance of the guard, which allows
// secure, encrypted cookie values to get generated for those cookies.
if (method_exists($guard, 'setCookieJar')) {
$guard->setCookieJar($this->app['cookie']);
}
if (method_exists($guard, 'setDispatcher')) {
$guard->setDispatcher($this->app['events']);
}
if (method_exists($guard, 'setRequest')) {
$guard->setRequest($this->app->refresh('request', $guard, 'setRequest'));
}
return $guard;
}
/**
* Get the guard configuration.
*
* @param string $name
* @return array
*/
protected function getConfig($name)
{
return $this->app['config']["auth.guards.{$name}"];
}
/**
* Get the default authentication driver name.
*
* @return string
*/
public function getDefaultDriver()
{
return $this->app['config']['auth.defaults.guard'];
}
guardの中身を読み解きましょう。
$nameに
“web”
が代入されます。
これは、getDefaultDriver()内で
config/auth.phpのdefaults.guardの値を参照しています。
その次に、resolveメソッドに”web”を渡します。
$config[‘driver’]は”session”なので、
$driverMethodにcreateSessionDriverが代入され、実行。
参考までにconfigファイルです。
- config/auth.php
/*
|--------------------------------------------------------------------------
| Authentication Defaults
|--------------------------------------------------------------------------
|
| This option controls the default authentication "guard" and password
| reset options for your application. You may change these defaults
| as required, but they're a perfect start for most applications.
|
*/
'defaults' => [
'guard' => 'web',
'passwords' => 'users',
],
/*
|--------------------------------------------------------------------------
| Authentication Guards
|--------------------------------------------------------------------------
|
| Next, you may define every authentication guard for your application.
| Of course, a great default configuration has been defined for you
| here which uses session storage and the Eloquent user provider.
|
| All authentication drivers have a user provider. This defines how the
| users are actually retrieved out of your database or other storage
| mechanisms used by this application to persist your user's data.
|
| Supported: "session", "token"
|
*/
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'token',
'provider' => 'users',
'hash' => false,
],
],
createSessionDriverメソッドの中で、
$guard = new SessionGuard($name, $provider, $this->app[‘session.store’]);
SessionGuardのインスタンスを作成して$guardに代入しています。
そこで、SessionGuardの中身をみてみると、
ついにloginメソッドがあります!
以下がSessionGuardのloginメソッドです。
/vendor/laravel/framework/src/Illuminate/Auth/SessionGuard.php
/**
* Log a user into the application.
*
* @param \Illuminate\Contracts\Auth\Authenticatable $user
* @param bool $remember
* @return void
*/
public function login(AuthenticatableContract $user, $remember = false)
{
$this->updateSession($user->getAuthIdentifier());
// If the user should be permanently "remembered" by the application we will
// queue a permanent cookie that contains the encrypted copy of the user
// identifier. We will then decrypt this later to retrieve the users.
if ($remember) {
$this->ensureRememberTokenIsSet($user);
$this->queueRecallerCookie($user);
}
// If we have an event dispatcher instance set we will fire an event so that
// any listeners will hook into the authentication events and run actions
// based on the login and logout events fired from the guard instances.
$this->fireLoginEvent($user, $remember);
$this->setUser($user);
}
/**
* Update the session with the given ID.
*
* @param string $id
* @return void
*/
protected function updateSession($id)
{
$this->session->put($this->getName(), $id);
$this->session->migrate(true);
}
セッションを更新(updateSession)します。
ログインイベントと認証済みイベントを実行して、
ログイン処理が終了になります。
最後に、設定したRedirectPathにリダイレクトして登録処理完了です。
ライブラリを読みこむのは勉強になる
本記事では、ユーザー登録処理の流れを解説しましたが、
いかかでしたか?理解が進んだでしょうか?
初心者にとってLaravelはとても便利です。
ただ便利さの反面、フレームワーク独自のコーディングルールやブラックボックスの部分が多いです。
使い始めた当初、
「なぜこういう動作をしているんだろう??」
気持ち悪さもありましたが、
最近標準のライブラリを読むことでLaravelを昔より深く理解できていると感じます。
綺麗なソースコードを読めますし、Laravelの動作原理も学べるので
ライブラリを読み込むのおすすめです。
それではまた!
コメント