search
LoginSignup
161

posted at

updated at

【Laravel】初めての認証(Laravel8でJetstreamを使う)

  • Laravel8でのJetstreamを使った、ユーザー管理機能の学習の記録。「 Jetstream 導入 と ユーザー登録/更新/ログイン機能 」についての備忘録です。
  • 参考:Laravel8Jetstream

環境

  • OS : macOS Caralina 10.15.7
  • PHP : 7.4.11
  • laravel : 8.11.2
  • Composer : 1.10.6

今回の学習内容

No. 概要
1. Jetstream の導入
・ 導入のコマンド
・ 日本語化する
・ 動作確認で、デフォルト機能を確認( 新規登録/ログイン(email+PW認証)、ユーザー情報更新)
2. 作成されたファイルの把握
※ 大量にファイルが作成されるので、理解が追いついてない。。
3. 例)基本の実装いろいろ
・ ログイン / アカウント登録後のリダイレクト先 の変更
・ usersテーブルへのカラム追加
・ user - 他モデル との紐付け
・ 所望のページにアクセス制限をつける
・ Jetstream ロゴの変更
・ アイコン写真の登録

Auth とは?

  • 認証(Ahthentication)や認可(Ahthorization)の意味。
  • Laravelでは、デフォルトで認証機能があり、コマンドで作れる。

Jetstream とは?

  • Laravel8の認証ライブラリ。
    • Laravel〜5:make:auth、Laravel6〜7:laravel/uiに分離。Laravel8では、全部入り(Jetstream)になったんだとか。。
  • Jetstream がやってくれることは、 ログイン、新規登録、メール検証、2段階認証、セッション管理、APIサポート(Laravel Sanctum)、チーム管理など。めっちゃやってくれる。。

1. Jetstreamの導入

【 導入のコマンド 】

Jetstreamの導入まで
// ① Jetstream のインストール
% composer require laravel/jetstream      // composerを使ってインストール
% php -d memory_limit=-1 /usr/local/bin/composer require laravel/jetstream     // メモリ制限エラーが出たら、メモリを上げてインストール
% laravel new project-name --jet          // アプリの雛形作成のタイミングで、Jetstream をインストールも可能(Livewire/Inertiaやチーム機能を使うか?聞かれる)
// インストールが完了したら、artisan コマンドでjetstreamが使えるようになる

// ② セットアップ : JSのパッケージをインストール( livewire と inertia を選択できる)
% php artisan jetstream:install livewire
% php artisan jetstream:install livewire --teams  // チーム機能を使う場合
% php artisan jetstream:install inertia          // vueやreactなどを使う場合は、コッチらしい。。

// ③ マイグレーション実行
% php artisan migrate            // usersテーブル、password_resetsテーブル作成のマイグレーションファイルが、自動生成されてる

// ④ JSとCSSをビルド
% npm install && npm run dev    // npmコマンドが無いエラーが出たら、Nodeのインストールが必要(https://nodejs.org/ja/)
  // 若しくは、別々にNode.js と NPM をインストール
  % npm install
  % npm run dev
// public/css/app.css、 public/js/app.js2ファイルが作成される
  • ここで、ブラウザで動作確認すると、エラー発生。'Laravel\Sanctum\HasApiTokens' が見つからんらしい。。

Trait 'Laravel\Sanctum\HasApiTokens' not found

→ laravel/sanctum をインストールしてみる。

laravel/sanctumのインストール
// ⑤ laravel/sanctumのインストール
% composer require laravel/sanctum
% php -d memory_limit=-1 /usr/local/bin/composer require laravel/sanctum   // メモリ制限エラーが出たら、メモリを上げてインストール
  • デフォルトでは、welcomeページに、ログイン・新規登録ボタンが設置してくれてる。が、今回は、既存アプリに導入したので、ルートページは、welcomeページではない。
    • welcomeページに設置された、「 ログイン・新規登録ボタン 」 部分をコピペさせてもらう。
welcomeページに設置された、ログイン・新規登録ボタン部分(welcome.blade.php)
@if (Route::has('login'))
  <div class="hidden fixed top-0 right-0 px-6 py-4 sm:block">
    @auth    // ログイン中の場合
      <a href="{{ url('/dashboard') }}" class="text-sm text-gray-700 underline">ダッシュボード</a>
    @else    // 未ログインの場合
      <a href="{{ route('login') }}" class="text-sm text-gray-700 underline">ログイン</a>
      @if (Route::has('register'))
        <a href="{{ route('register') }}" class="ml-4 text-sm text-gray-700 underline">アカウント作成</a>
      @endif
    @endif
  </div>
@endif
  • @auth...@endif構文(※ Laravel7までは、@auth...@endauth)。

【 日本語化する 】

フィールドの日本語化

  1. バリデーションなどの翻訳データが公開されてるので、GitHubのLaravel-Lang/lang から、zipファイルをダウンロード。
  2. ダウンロードしたフォルダの中から、日本語化ファイルを移動↓
- コマンドで実行する場合は、↓
日本語化設定(resources/lang/jaの生成)
php -r "copy('https://readouble.com/laravel/6.x/ja/install-ja-lang-files.php', 'install-ja-lang.php');"
php -f install-ja-lang.php
php -r "unlink('install-ja-lang.php');"
// resources/lang/ja に、ファイルが生成される( auth.php、 pagination.php、 passwords.php、 validation.php )

※ ただし、微妙に英語が残ってるので、気付いた箇所は、resources/lang/js.json に、力技で日本語化。

【 動作確認 】

  • この段階で、新規登録とログイン機能が動く状態になってる。
  • ルートページに、ログイン・新規登録ボタンが出現。
新規登録ボタン → 新規登録ページ ログインボタン → ログインページ
localhost:8000/register localhost:8000/login
↓ Already registered? をクリック
(localhost:8000/login)
↓ Forgot your password? をクリック
(localhost:8000/forgot-password)
ログインページへ遷移。
PWリセットリンクをメールで送信してくれる。
  • name、email、PWは必須。
    • name、emailは、Max255文字まで。
    • emailは、文字列長(@を含む)と、一意性。
    • PWは、8文字 / 空 / PW(確認用)と不一致 で、バリデーション。
    • バリデーションの条件は、app\Actions\Fortify\CrateNewUser.php、PasswordValidationRules.php に書かれてる。
  • ログイン時に、入力を何度も間違うと、一定時間ログインできなくなる。
  • アカウント作成クリックで、DBにデータが保存できてる。
  • ログイン / アカウント作成 をクリック → dashboardページに遷移する。
dashboardページ ログイン状態 → ルートページ
localhost:8000/dashboard localhost:8000/

ログイン時のボタン表示
  • dashboardページには、Profile・ログアウトボタンが設置されてる。
Profileボタン → ユーザー情報更新ページ ログアウトボタン
localhost:8000/user/profile localhost:8000

ログイン中の端末もわかる。

ルートページへ遷移。
(未ログイン時のボタン表示)
  • ユーザー情報(name、email、photo)やPWの更新に関するバリデーション条件は、app\Actions\Fortify\UpdateUserProfileInfomation.php、UpdateUserPassword.php(※ PasswordValidationRules.phpを呼び出してる) に書かれてる。

2. 作成されたファイルたちの一部

○ ルーティング

  • 認証用のルートが追加される。
  • authミドルウェアによって、未ログイン時は、ログインページにリダイレクトされる仕様。
追加される認証用のルート
// web.php
Route::middleware(['auth:sanctum', 'verified'])->get('/dashboard', function () {
    return view('dashboard');
})->name('dashboard');   // dashboard というルート名が付けられてる

// api.php
Route::middleware('auth:sanctum')->get('/user', function (Request $request) {
    return $request->user();
});

○ コントローラー ( app\Http\Controllers)

  • Jetstream では、不要なので作成されない。必要に応じて、自分で作成する。
例)app/Http/Controllers/Auth/LoginController.phpの作成の場合
% php artisan make:controller Auth\\LoginController

○ ビュー ( resources\views )

  • Jetstreamが作成した、コンポーネントファイルは、隠しファイルになってる。 ↓のコマンドで表示でき、編集可能になる。
隠しファイルの表示コマンド
% php artisan vendor:publish --tag=jetstream-views
// resources/views/vendor/jetstream ディレクトリが出現する
ファイル名 用途
auth\forgot-password.blade.php PWリセットリンクの送信先入力フォーム
auth\login.blade.php ログインページ
layouts\guest.blade.phpを読み込んでる。
auth\register.blade.php 新規登録ページ
layouts\guest.blade.phpを読み込んでる。
auth\reset-password.blade.php PWリセット
auth\two-factor-challenge.blade.php ログインページ(2段階認証)
auth\verify-email.blade.php 本人確認ページ(2段階認証)
layouts\app.blade.php ログイン時のレイアウト
(ナビゲージョンバー、ヘッダーを呼び出してる)
layouts\guest.blade.php 未ログイン時のレイアウト(ログイン・新規登録ページ)
profile\●●.blade.php プロフィール編集ページに使ってる部分テンプレート
vendor\jetstream\components\●●.blade.php(隠しディレクトリ) コンポーネントファイル(各種部分テンプレート)
welcome.blade.php welcome画面に認証用ボタンが設置される。
vendor\jetstream\components\welcome.blade.phpからコンポーネントを読み込んでる。
dashboard.blade.php ダッシュボードページ
layouts\app.blade.php、welcome.blade.phpを読み込んでる。
navigation-dropdown.blade.php ナビゲージョンバー
  • @componentで呼び出していたコンポーネントを<x-xxxx />の形式で呼び出す仕組み。Livewireでは、全力でコンポーネントを使い倒してる。。
    • x-タグは、Bladeコンポーネント。x-jet-タグは、Jetstream内で定義されてるコンポーネント。
    • 例えば、<x-app-layout>は、app/View/Components/AppLayout.php を読み込んでる。
      このファイルでは、resources/views/layouts/app.blade.php のビューを呼び出してるカタチになってる。
    • 例えば、<x-jet-welcome />は、Jetstreamの隠しファイル内のresources/views/vender/jetstream/components/welcome.blade.php を読み込んでる。

○ モデル ( app\Models\User.php )

作成されたUserモデルの中身
// 値を代入するフィールドの設定
protected $fillable = ['name', 'email', 'password',];
// データを取得しないフィールドの指定
protected $hidden = ['password', 'remember_token', 'two_factor_recovery_codes', 'two_factor_secret',];
// キャストが必要なフィールドの指定
protected $casts = ['email_verified_at'=>'datetime',];
// モデルの配列に追加
protected $appends = ['profile_photo_url',];

○ 認証機能の設定 ( config\auth.php )

認証設定ファイル(config/auth.php)の中身
return [
  'defaults' => [                 // デフォルトの認証「ガード」と、PWリセットオプション
    'guard' => 'web',
    'passwords' => 'users',
  ],
  'guards' => [
    'web' => [
      'driver' => 'session',
      'provider' => 'users',
    ],
    'api' => [
      'driver' => 'token',
      'provider' => 'users',
      'hash' => false,           // APIトークンを平文ではなく SHA-256 にする場合は true
    ],
  ],
  'providers' => [
    'users' => [
      'driver' => 'eloquent',              // データへのアクセスで使用するドライバー
      'model' => App\Models\User::class,   // 指定したドライバーが eloquent の場合、使うモデル
    ],
  ],
  'passwords' => [
    'users' => [
      'provider' => 'users',              // 使うproviderの設定
      'table' => 'password_resets',       // PWリセットトークンの格納テーブル
      'expire' => 60,                     // 有効時間
      'throttle' => 60,                   // 要求制限、スロットル時間(秒)
    ],
  ],
  'password_timeout' => 10800,           // PW再確認までの時間
];

○ プロバイダー ( app\Providers )

  • Jetstream を導入すると、認証に関するFortify、Jetstream プロバイダーが追加される。

○ テーブル

  • users テーブル : ユーザ管理用
カラム名 カラム型 用途
name string ユーザー名
email string(一意性) メールアドレス
email_verified_at timestamp メールによる本人確認日時
password string パスワード
two_factor_secret text(null可) 2段階認証
two_factor_recovory_codes text(null可) リカバリーコード
remember_token remember_token クッキー情報
current_team_id foreignId(null可) チーム機能で使用
profile_photo_path text(null可) プロフィールのアイコン写真のパス
  • password_resets テーブル : PWリセット管理用
カラム名 カラム型 用途
email string(インデックス) メールアドレス
token string PWリセットトークン
  • sessions テーブル : セッション管理用
カラム名 カラム型 用途
id string(一意性) ユーザー識別用のラベルみたいなヤツ
user_id foreignId(一意性、インデックス) ユーザーid(外部キー)
ip_address string(null可) アクセスしたIPアドレス
user_agent text(null可) アクセスしたブラウザ情報
payload text Sessionに保存する値(base64でエンコードされてる)
last_activity integer(インデックス) 最後のアクセス日時(unixタイムスタンプ)

3. 例)基本の実装いろいろ

【 ログイン / アカウント登録後のリダイレクト先 の変更 】

  • デフォルトだと、ログインやアカウント登録 → localhost:8000/dashboard にリダイレクトされる。
  • 認証ユーザーリダイレクトの設定ファイル(app\Http\Middleware\RedirectIfAuthenticated.php) → 定義元(app\Providers\RouteServiceProvider.php)を呼び出してるカタチ。
app\Http\Middleware\RedirectIfAuthenticated.php
public function handle(Request $request, Closure $next, ...$guards) {
  $guards = empty($guards) ? [null] : $guards;
  foreach ($guards as $guard) {
    if (Auth::guard($guard)->check()) {
      return redirect(RouteServiceProvider::HOME);   // RouteServiceProvider を呼び出してる
    }
  }
  return $next($request);
}

なので、定義元の方を変更してみる。

ログインやアカウント登録後のリダイレクト先の変更(app\Providers\RouteServiceProvider.php)
public const HOME = '/home';        // Jetstream導入前
public const HOME = '/dashboard';   // Jetstream導入後
   // rootページに変更
public const HOME = '/';

【 usersテーブルへのカラム追加 】

  • 登録項目を追加してみる。
新規登録ページ プロフィール編集ページ
localhost:8000/register localhost:8000/user/profile
  • まずは、通常通り、マイグレーションファイルを作成し、usersテーブルにカラムを追加。
usersテーブルにカラム追加
% php artisan make:migration add_column_to_users_table --table=users

// マイグレーションファイルに、追加カラムを記述
public function up() {
  Schema::table('users', function (Blueprint $table) {
    $table->date('birth')->nullable();
  });
}
public function down() {
  Schema::table('users', function (Blueprint $table) {
    $table->dropColumn('birth');
  });
}

% php artisan migrate      // マイグレーション実行

次に、新規登録時に、データを保存できるよう、修正。

新規登録のための修正
// Userモデル
protected $fillable = [
     :
  'birth',
];

// app\Actions\Fortify\CreateNewUser.php
Validator::make($input, [
      : 
  'birth' => ['nullable', 'date', 'before:yesterday'],   // null or 昨日以前 でない場合は、バリデーション
])->validate();
return User::create([
      :
  'birth' => $input['birth'],
]);

// ビューに入力フォームを設置(resources\views\auth\register.blade.php)
<div class="mt-4">
  <x-jet-label for="birth" value="{{ __('生年月日(※ 任意)') }}" />
  <x-jet-input id="birth" class="block mt-1 w-full" type="date" name="birth" required />
</div>
  • 同様に、プロフィール編集ページにも反映。
プロフィール編集ページの修正
// app\Actions\Fortify\UpdateUserProfileInformation.php
Validator::make($input, [
         :
  'birth' => ['required', 'date', 'before:yesterday'],    // 削除メソッド未実装なので、とりあえず、空にできないよーにしとく。。 
])->validateWithBag('updateProfileInformation');

if ($input['email'] !== $user->email &&
         :
} else {
  $user->forceFill([
         :
    'birth' => $input['birth'],
  ])->save();
}

// ビュー(resources\views\profile\update-profile-information-form.blade.php)
<div class="col-span-6 sm:col-span-4">
  <x-jet-label for="birth" value="{{ __('生年月日') }}" />
  <x-jet-input id="birth" type="date" class="mt-1 block w-full" wire:model.defer="state.birth" />
  <x-jet-input-error for="birth" class="mt-2" />
</div>

【 モデルの紐付け 】

  • 例えば、記事投稿にユーザーを紐付ける場合。
    • 紐付けたいテーブルに、user_idカラムを作成し、モデルにアソシエーションを追記。
Userモデルとのアソシエーション
public function user() {
  return $this->belongsTo('App\Models\User');
}
コントローラーで取得して、ビューで表示してみる
// モデルを紐づけたので、コントローラーでは、Article::all()で、ユーザー情報も取得できてる
public function index() {
  $articles = Article::all();
  return view('index', ['articles' => $articles] );
}

// ビューで呼び出す
@foreach ($articles as $article)
  <p>{{ $article -> user -> name }}</p>    // 紐づくユーザー名 を表示
@endforeach

【 アクセス制限 】

  • 例えば、投稿・編集・削除機能は、ログイン時のみに制限したい。
コントローラー
class ArticleController extends Controller {
  // 未ログイン時は、index、showのみ許可する(それ以外では、未ログインなら、loginページへリダイレクト)
  public function __construct() {      //  __construct クラスを追加
    $this->middleware('auth')->except(['index', 'show']);
  }

  // 投稿機能
  public function store(Request $request){
    $article = new Article();
    $user = \Auth::user();
    $article -> content = request('content');
    $article -> user_id = $user->id;
    $article -> save();
    return redirect() -> route('article.detail', ['article' => $article->id] );
  }

  // 投稿者のみ、詳細表示ページに、編集・削除ボタンを表示させたいので、ビューでの条件分岐のために、変数で定義
  public function show($id){
    $article = Article::find($id);
    $user = \Auth::user();
    if ($user) {
      $login_user_id = $user->id;
    } else {
      $login_user_id = "";
    }
    return view('show', ['article' => $article, 'login_user_id' => $login_user_id] );
  } 
}
ビュー
// ログイン時のみ、登録ボタンを表示する
@auth
  <div>
    <a href='{{ route("article.new") }}'>登録ボタン</a>
  </div>
@endauth

// 投稿者のみ、編集ボタン・削除ボタンを表示する
@auth
  @if ($article -> user_id == $login_user_id)   // 現在のユーザーid = 投稿者id の場合
    <a href = '{{ route("article.edit", ["article" => $article->id]) }}', class='btn'>編集ボタン</a>
    {{ Form::open(['method' => 'delete', 'route' => ['article.destroy', $article->id] ]) }}
      {{ Form::submit('削除ボタン', ['class' => 'btn']) }}
    {{ Form::close() }}
  @endif
@endauth
  • 動作確認用に、usersテーブルにダミーデータを作成しとくとイイかも。
ダミーデータの作成
// Seederに記述(database/seeders/DatabaseSeeder.php)
          :
use App\Models\User;                // Userモデルを使うよー
class DatabaseSeeder extends Seeder {
  public function run() {
    User::factory(5)->create();    // ダミーデータを5つ作成
  }
}

// ダミーデータの作成実行(ターミナル)
% php artisan db:seed

【 Jetstream のロゴ変更 】

  • ロゴを含んでるページは、ログインページ、新規登録ページ、ダッシュボードページなど。
ビュー デフォルト 変更後
ログインページ
新規登録ページ
ダッシュボードページ
プロフィール情報更新ページ
  • 今回は、FontAwesomeのアイコンを使うので、FontAwesome5を導入。ダウンロード or CDN リンクをビューファイルに貼りつける方法があるが、手っ取り早く、後者で。
参)FontAwesomeの導入方法
// layout.blade.php の <head>タグに記述
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.15.1/css/all.css">
// 使い方
<i class="fas fa-map-pin"></i>

ログイン、新規登録ページのロゴを変更する

  • layout ファイルは、resources/views/layouts/guest.blade.php 。
  • ロゴ部分のコードは、Jetstreamのコンポーネントにある(resources/views/vendor/jetstream/components/authentication-card-logo.blade.php )。
  • それぞれ、resources/views/auth/login.blade.php と register.blade.php で読み込んで使ってるので、コンポーネント側の記述を変更。
ログインページ、新規登録ページのロゴの変更
// FontAwesomeを使えるよーにする(resources/views/layouts/guest.blade.php の <head>タグ内に記述)
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.15.1/css/all.css">

// FontAwesomeのアイコンに変更してみる(resources/views/vendor/jetstream/components/authentication-card-logo.blade.php)
<a href="/">
  <h1>Lunch Map</h1>
  <i class="fas fa-map-pin"></i>
</a>

ダッシュボード、プロフィール情報更新ページのロゴを変更する

  • layout ファイルは、resources/views/layouts/app.blade.php 。
  • ロゴ部分のコードは、Jetstreamのコンポーネントにある(resources/views/vendor/jetstream/components/authentication-card-logo.blade.php )。
  • それぞれのページのヘッダー部分でナビゲージョンバー(resources/views/navigation-dropdown.blade.php) を読み込んでる。さらに、ナビゲージョンバーで、Jetstreamのコンポーネントを読み込んでる。
ダッシュボード、プロフィール情報更新ページのロゴの変更
// FontAwesomeを使えるよーにする(resources/views/layouts/app.blade.php の <head>タグ内に記述)
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.15.1/css/all.css">

// (方法1) コンポーネントのロゴ部分を変更(resources/views/vendor/jetstream/components/application-mark.blade.php)
<i class="fas fa-map-pin fa-2x"></i>
// ヘッダーのナビゲーション部分(resources/views/navigation-dropdown.blade.php)
<a href="{{ route('article.list') }}">   // リンク先も自分の設定したページに変更(※ デフォルトは、ダッシュボード)
  <x-jet-application-mark class="block h-9 w-auto" />
</a>
// (方法2) 若しくは、ナビゲーション部分を直接変更する(resources/views/navigation-dropdown.blade.php)
<a href="{{ route('article.list') }}">
  <i class="fas fa-map-pin fa-2x"></i>
</a>

【 アイコン写真の登録 】

  • Jetstream導入時に、デフォルトで、usersテーブルに写真データ用のカラムが作成されてる。
  • Jeststream公式リファレンスを参考に、config/jetstream.php で、該当箇所のコメントアウトを外し、アイコン写真を有効化。
コメントアウトを外して有効化(config\jeststream.php)
'features' => [
  Features::profilePhotos(),
  Features::api(),
  Features::teams(),
],
  • 写真データの保存場所は、 public\strage\profile-photos 。
    usersテーブルのprofile_photo_path カラムで、ココに格納されたデータを呼び出してる。
保存場所へシンボリックリンクを貼る必要がある
% php artisan storage:link

「.env」ファイルのURLも環境に合わせて変更(※ 今回はローカル環境)。

.envファイル
APP_URL=http://127.0.0.1:8000

(エラー) profile_photo_path カラムがないよ!

jetstream sqlstate[42s22]: column not found: 1054 unknown column 'profile_photo_path' in 'field list'

  • (状況)今回、アプリ実装途中で、Jetstreamを導入した。既にマイグレーションを何度か実行しており、usersテーブルが作成されてる状態だった。
    • Jetstream 導入によって、マイグレーションファイル(xxxxx_create_users_table.php)に、↓の記述が追加されるため、既に実行済みのマイグレーションファイルに追記されてしまい、カラムが作成できてなかったのが原因。
xxxxx_create_users_table.php
// Jetstream 導入で、追加されたカラム
$table->foreignId('current_team_id')->nullable();
$table->text('profile_photo_path')->nullable();
  • なので、マイグレーション 初期化 → 再実行で、profile_photo_path カラムが作成でき、解決。
マイグレーションを初期化→再実行
% php artisan migrate:refresh

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
161