🔑

laravelのセッションによるログイン判定を本気で理解する(Part3.ログイン判定編)

2023/01/17に公開

はじめに

前回は作成されたセッションにログインされるとuser_idがセットされるところを見ました。
今回はユーザーがログイン済みかどうかをセッションをつかって判定している箇所を調べます。

前回

https://zenn.dev/cube/articles/7d509c01cc82a6

ログイン判定

laravelのログイン判定と言えばルーティング」でmiddlewareをauthとしてやってあげるやり方ですが、middlewareのauthを使うとApp\Http\Kernel.phpのauthに紐づいているクラスが起動します。

App\Http\Kernel.php

class Kernel extends HttpKernel
{
    ...
    protected $routeMiddleware = [
        'auth' => \App\Http\Middleware\Authenticate::class,
         ...
    ];

つまりAuthenticateのhandleを見ればいいわけですね。
なぜ上手くauthが起動してくれるかはルーティングとミドルウェア編でやります。
今回は上手くmiddleware:authを指定したルートを通るときはAuthenticateが起動するとして話を進めます。

\App\Http\Middleware\Authenticateにはhandleがないので、継承元の Illuminate\Auth\Middleware\Authenticateへ


Illuminate\Auth\Middleware\Authenticate.php
public function __construct(Auth $auth)
    {
        $this->auth = $auth;
    }

    public function handle($request, Closure $next, ...$guards)
    {
        $this->authenticate($request, $guards);

        return $next($request);
    }

protected function authenticate($request, array $guards)
    {
        if (empty($guards)) {
            $guards = [null];
        }

        foreach ($guards as $guard) {
            if ($this->auth->guard($guard)->check()) {
                return $this->auth->shouldUse($guard);
            }
        }

        $this->unauthenticated($request, $guards);
    }

メソッドをたどっていくとthis->auth->guard($guard)->checkが本命みたいですね。
ここの$guardsはmiddleware(auth)とすればempty,middleware(auth:user)とすればuserが入ります。
なぜそうなるのかはルーティング編で。

$this->authは\Illuminate\Contracts\Auth\Factoryですが、サービスコンテナを使って依存解決しています。
サービスコンテナとauth絡みについては下記で。
https://zenn.dev/cube/articles/d7b978c5eabde5

上記で説明している通り、
\Illuminate\Contracts\Auth\Factoryは\Illuminate\Auth\AuthManager::classと依存解決されるので、
$this->authはAuthManagerを見ます。

AuthManager->guardについてはpart2でやった通りSessionGuardが入ります。
https://zenn.dev/cube/articles/7d509c01cc82a6

なのでユーザーがログイン済みかの判定はSessionGuardのcheck()を見ればいいことになりますね。
SessionGuardではなく継承元のGuardHelpersにcheckがあるので注意。

Illuminate\Auth\GuardHelpers.php

 public function check()
    {
        return ! is_null($this->user());
    }

$this->userはSessionGuardにあるので戻ります。

Illuminate\Auth\SessionGuard.php

  public function user()
    {
        ...
	
        $id = $this->session->get($this->getName());

        $this->user = $this->provider->retrieveById($id)) 
        
	...
        return $this->user;
    }
    
    public function getName()
    {
        return 'login_'.$this->name.'_'.sha1(static::class);
    }

上記の操作はpart2で見たことがありますね。
$key = login_web_???? (???はsha1でハッシュされたもの)
$value = user_id(1とか)
をログイン処理の時、セッションにセットしたことを思い出してください。
今回はlogin_web_????を用いてuser_idをセッションから取り出しています。

$this->provider->retrieveByIdでユーザーをuser_idを使って取り出していますね。

上記の詳細な説明は前回であるpart2で。

ここでauthenticateに戻ると、


protected function authenticate($request, array $guards)
    {
        if (empty($guards)) {
            $guards = [null];
        }

        foreach ($guards as $guard) {
            if ($this->auth->guard($guard)->check()) {
                return $this->auth->shouldUse($guard);
            }
        }

        $this->unauthenticated($request, $guards);
    }
    
    
protected function unauthenticated($request, array $guards)
    {
        throw new AuthenticationException(
            'Unauthenticated.', $guards, $this->redirectTo($request)
        );
    }

もしうまくユーザーが取り出せない場合、checkがnullになるので$this->unauthenticatedに入ります。
例外が投げられてここで終了となり、うまくログイン判定がされることがわかりました。

おわりに

セッションとCookieの仕組みについては理解したかな?という感じだったのですが、
laravelではふわふわした理解でどうやってユーザーがログインしているかしていないかを見ているのかわからなかったのが発端で記事を書きました。

多分最初にlaravelを呼んだ時サービスコンテナやcreate???Driver周りが原因でどうなってるかわからなくなったりすると思うのですが、この記事+サービスコンテナの記事で解決できればうれしい思いです。

Discussion