演習課題「日記アプリに、ログインページへのリンクを追加」
右の環境には、Laravelで「mydiary」ディレクトリに日記アプリを作成してあります。また、ユーザー管理用のルートとビューも自動生成してあります。
このアプリの共通テンプレート(layout.blade.php)のナビゲーションバーに、ユーザー登録とログインのリンクを追加してください。
採点して、すべてのジャッジに正解すれば、演習課題クリアです!
※ 採点時は、サーバを起動し、問題文に関するページにアクセスできる状態にしてください。
#05:ログイン機能を追加しよう
ここからは、Lunchmapアプリにユーザー管理機能を組み込んでいきます。まずは、ユーザー管理用のルートとビューを追加して、ユーザー登録とログイン機能を有効にしましょう。
$ php artisan make:auth
resources/views/layout.blade.php<head>
<meta charset='utf-8'>
<meta name='viewport' content='width=device-width, initial-scale=1, shrink-to-fit=no'>
<meta name='csrf-token' content='{{ csrf_token() }}'>
<link rel='stylesheet' href='https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css' >
<title>Lunchmap</title>
<style>body {padding-top: 80px;}</style>
<script src='{{ asset("js/app.js") }}' defer></script>
</head>
resources/views/layout.blade.php<nav class='navbar navbar-expand-md navbar-dark bg-dark fixed-top'>
<a class='navbar-brand' href={{route('shop.list')}}>Lunchmap</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="{{ __('Toggle navigation') }}">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<!-- Left Side Of Navbar -->
<ul class="navbar-nav mr-auto">
</ul>
<!-- Right Side Of Navbar -->
<ul class="navbar-nav ml-auto">
<!-- Authentication Links -->
@guest
<li class="nav-item">
<a class="nav-link" href="{{ route('login') }}">{{ __('Login') }}</a>
</li>
@if (Route::has('register'))
<li class="nav-item">
<a class="nav-link" href="{{ route('register') }}">{{ __('Register') }}</a>
</li>
@endif
@else
<li class="nav-item dropdown">
<a id="navbarDropdown" class="nav-link dropdown-toggle" href="#" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" v-pre>
{{ Auth::user()->name }} <span class="caret"></span>
</a>
<div class="dropdown-menu dropdown-menu-right" aria-labelledby="navbarDropdown">
<a class="dropdown-item" href="{{ route('logout') }}"
onclick="event.preventDefault();
document.getElementById('logout-form').submit();">
{{ __('Logout') }}
</a>
<form id="logout-form" action="{{ route('logout') }}" method="POST" style="display: none;">
@csrf
</form>
</div>
</li>
@endguest
</ul>
</div>
</nav>
動画ではresources/views/layout.blade.php
のheadタグ内にcsrf_token()
という記述をしています。
これはCross Site Request Forgery(リクエスト強要)と呼ばれる攻撃手法に対策するものです。
CSRFはWebアプリケーションへのリクエストを記述した命令を不本意に実行させて、その人の権限でリクエストを実行させるという攻撃です。
例えば本講座ではPOSTメソッドを使ってLunchmapアプリにお店を追加しますが、
1. お店を追加するPOSTリクエストを記述したボタンのある偽のサイトを用意する
2. 攻撃対象のユーザーに偽のサイトへアクセスさせる
3. 攻撃対象のユーザーが(1)のボタンをクリックしてしまう
4. 攻撃対象のユーザーがLunchmapアプリにログイン中だった場合、クリックしたボタンのPOSTメソッドが受理されてしまい、意図せずお店が追加されてしまう
という流れで攻撃が成立してしまいます。
実際に存在するSNSで、CSRFを利用してユーザーの知らないところで不本意な投稿を大量に繰り返されてしまうという事件がありました。
このことの対策として、一回使い切りの認証情報を作ることで毎回「本当に本人が要求したリクエストなのか?」ということを確認する方法があります。
この認証情報は毎回変わりますので事前に偽のリクエストを作ることが難しくなりますし、最悪認証情報を盗まれてしまったとして使い捨てなのでその時点ではもう使用済みで破棄されているから大丈夫、ということになります。
Laravelではこの「一回使い切りの認証情報」を簡単に利用できるようになっています。一回使い切りの認証情報を自動で作成し、その認証情報が正しいかの照合まで行ってくれるのが「csrf_token()」という機能なのです。
layout.blade.php
のheadタグで、<script src='{{ asset("js/app.js") }}' defer></script>
という記述をしています。
ここでapp.js
を読み込んでいるのですが、読み込まれているapp.js
はpublic/js/app.js
にあります。
しかし、このapp.js
は別のスクリプトをコンパイルしてブラウザで表示するように圧縮されたものであり、その圧縮する前の本体であるスクリプトはresources/js/app.js
となります。
本体のapp.jsの8行目で同じディレクトリにあるbootstrap.js
が呼び出されていますが、このbootstrap.js
の中に上の項で解説したcsrf_tokenの認証に関わる処理が記述されており(27-39行目)、csrf_tokenを利用するために読み込む必要のあるスクリプトなのです。
ただし、このapp.jsはWebページがすでに読み込まれている前提で動作します。そのためページの読み込みが終わる前にapp.jsが実行されてしまうとエラーが発生してしまうので、defer
を使ってページが読み込まれてから遅れて実行されるように制御しています。
- 認証 5.7 Laravel
https://readouble.com/laravel/5.7/ja/authentication.html
- Laravelの標準Authentication(Auth)の動きを調べてみる - Qiita
https://qiita.com/zaburo/items/9fcf0f4c771e011a4d35
- Laravel 5.5 の認証機能とそのカスタマイズ - Qiita
https://qiita.com/naga3/items/4f3defde59b31a1a797e
- <script> タグに async / defer を付けた場合のタイミング - Qiita
https://qiita.com/phanect/items/82c85ea4b8f9c373d684