Laravel Authに既存Userテーブルでログインする

Laravel Authは便利だけども

Laravelはphp artisan make:authなどのコマンドで一瞬でログイン周りのviewやログイン機構ができてとても便利ですが、このLaravel Authを使って既存user DBへログインさせようとするとアプリケーションに依存したハッシュがかかっていたり、カラムが違ったりなどとそのまま利用するとパスワード認証が通りません。

Authで作成されたログイン周りの仕組みに限りませんが、Laravelのフレームワーク内部にある継承したクラスのメソッドは上書きしてカスタマイズできるように設計されています。基本的には①認証に必要なカラムを変更する(AuthのデフォルトではUserモデルのemail/passwordのカラムを利用)②パスワードのハッシュ方法を既存DBに合わせる(Authのデフォルトではパスワードをハッシュ化して既存DB値と比較)をできれば既存DBでログインできるようになります。

今回はLaravel8で行いUserモデル、Loginビュー、Loginコントローラー、とログイン周りのMVCをいじっていきます。そんなにLaravelのバージョンで大きく変わらないかなとは思ってます。他にも調べると色んな方法があったので経験積んで引き出し増やしたいですね。

本気で詳細を理解したい人向けのLaravelログイン認証
Hashファサード

Userモデル

対象とするDBや主キーなどの情報を明記していくDB接続のやり方と一緒ですね。

App\Models\Users\.php
<?php

namespace App\Models;

use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Sanctum\HasApiTokens;

class User extends Authenticatable
{
    use HasApiTokens, HasFactory, Notifiable;

    // @override
    protected $table = 'your_user_table';
    protected $primaryKey = 'your_column(主キー)';
   //主キーがauto incrementでなくtimestampが無い場合
    public $incrementing = false;
    public $timestamps = false;
    

    /**
     * The attributes that are mass assignable.
     *
     * @var string[]
     */

    protected $fillable = [
        'your_column(idやメールなど)',
        'your_pass(passwordなど),
        //その他カラムがあれば
        'your_column3',
    ];

    /**
     * The attributes that should be hidden for serialization.
     *
     * @var array
     */
protected $hidden = [
        'your_column',
        'your_pass',
    ];

}

Loginコントローラー

色々やり方もあると思いますし、ここがポイントです。usernameメソッドでデフォルトの代わりとなるカラムを指定してあげます。credidentialメソッド使用するカラムを、attemptLoginメソッドで実際に行いたい認証の処理を書いてtrueを返すようにするとログインできたということにします。

App\Http\Controllers\Auth\LoginController.php
<?php
namespace App\Http\Controllers\Auth;

use App\Http\Controllers\Controller;
use App\Providers\RouteServiceProvider;
use Illuminate\Foundation\Auth\AuthenticatesUsers;

//色々インポート
use Illuminate\Support\Facades\Log;
use Illuminate\Http\Request;
use App\Models\User;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Auth;


class LoginController extends Controller
{
    /*
    |--------------------------------------------------------------------------
    | Login Controller
    |--------------------------------------------------------------------------
    |
    | This controller handles authenticating users for the application and
    | redirecting them to your home screen. The controller uses a trait
    | to conveniently provide its functionality to your applications.
    |
    */

    use AuthenticatesUsers;

    // @override
    protected $redirectTo = '/your_app_url';  // ログイン後のリダイレクト先
    protected $errorMessage = ' ';  // エラーメッセージ


    /**
     * Create a new controller instance.
     *
     * @return void
     */
    public function __construct()
    {
        $this->middleware('guest')->except('logout');
    }

    // @override
    public function username()
    {
        return 'your_column1';
    }

    // @override
    protected function credentials(Request $request)
    {
        return $request->only($this->username(), 'your_pass');
    }

    // @override
    protected function validateLogin(Request $request)
    {
        $this->validate($request, [
            'your_pass' => 'required|string',
        ]);
    }

    // @override
    // ログイン試行
    public function attemptLogin(Request $request)
    {
        try
        {
            Log::debug('ログインリクエスト : '.$request->your_column);
            $user = User::where('user_id', $request->your_column)
                        ->where(DB::raw('your_hash_method(your_pass)') , your_hash_methco($request->user_pass))
                        ->first();
            if(!isset($user))
            {
                return false;
            }

            /*
            * ログイン時に必要な処理はここに書く
            *  EX) session()->put('test_session', 'test');
            */

            Auth::login($user);
            Log::debug('ログイン成功 : '.$request->user_id);
            return true;
        }
        catch (\PDOException $qe)
        {
            Log::error('DB接続エラー発生');
            report($qe);
            $this->errorMessage = 'DB接続エラー : 時間をおいて再試行してください';
            return false;
        }
        catch (\Exception $e)
        {
            Log::error('予期せぬエラー発生');
            report($e);
            $this->errorMessage = '予期せぬエラー : システム管理者に報告してください';
            return false;
        }
    }

    // @override
    // ログイン失敗
    protected function sendFailedLoginResponse(Request $request)
   {
       Log::debug('ログイン失敗 : '.$request->your_column);
       $failMessage;
       if($this->errorMessage === '')
       {
           $failMessage = 'ユーザーIDまたはパスワードが違います';
       }
       else
       {
           $failMessage = $this->errorMessage;
       }       
       return view('auth.login')
            ->with('failMessage',$failMessage);
   }

    // @override
    // ログアウト時の処理
    protected function loggedOut(Request $request)
    {
        // 画面遷移先
        return redirect('/your_app_url/login');
    }
}

Loginビュー(blade)

フォームのidとエラーメッセージ部分を修正してあげます。

resources/views/auth/login.blade.php
@extends('layouts.app')

@section('content')
<div class="container">
    <div class="row justify-content-center align-items-center" style="height:75vh">
        <div class="col-8">
            <div class="card">
                <div class="card-header">{{ __('ログイン') }}</div>
                <div class="card-body">
                    <form method="POST" action="{{ route('login') }}">
                        @csrf
                        
                        <div class="form-group row">
                            <label for="email" class="col-md-4 col-form-label text-md-right">{{ __('ユーザーカラム') }}</label>
                            <div class="col-md-6">
                                <input id="your_column" type="text" class="form-control @error('your_column') is-invalid @enderror" name="your_column" value="{{ old('your_column') }}" required autocomplete="your_column" autofocus>
                            </div>
                        </div>
                        
                         
                        <div class="form-group row">
                            <label for="password" class="col-md-4 col-form-label text-md-right">{{ __('パスワード') }}</label>
                            <div class="col-md-6">
                            <input id="your_pass" type="password" class="form-control @error('your_pass') is-invalid @enderror" name="your_pass" required autocomplete="current-password">
                                @if(isset($failMessage))
                                    <div>
                                        <strong style="color:red">{{ $failMessage }}</strong>
                                    </div>
                                @endif
                            </div>
                        </div>
                        
                        
                        <div class="form-group row">
                            <div class="col-md-6 offset-md-4">
                                <div class="form-check">
                                    <input class="form-check-input" type="checkbox" name="remember" id="remember" {{ old('remember') ? 'checked' : '' }}>
                                    <label class="form-check-label" for="remember">
                                        {{ __('Remember Me') }}
                                    </label>
                                </div>
                            </div>
                        </div>
                        

                        <div class="form-group row mb-0">
                            <div class="col-md-8 offset-md-4">
                                <button type="submit" class="btn btn-primary">
                                    {{ __('ログイン') }}
                                </button>
                                
                                @if (Route::has('password.request'))
                                    <a class="btn btn-link" href="{{ route('password.request') }}">
                                        {{ __('Forgot Your Password?') }}
                                    </a>
                                @endif
                                

                            </div>
                        </div>
                    </form>
                </div>
            </div>
        </div>
    </div>
</div>
@endsection

まとめ

以上簡易的なLaravel Authカスタム案でした。最近では外部認証なども利用するログイン方法が多いので、これだけだと不足ですし、新規登録はいつか別の記事で書いていきたいと思います。また、正直コードが見づらいブログの書き方・色合いになってしまってますね。改善していきます。

おすすめの記事