九保すこひ@フリーランスエンジニア|累計300万PVのブログ運営中
さてさて、Laravel 7.x
の新機能を紹介しはじめてすでに第6回目になりました。今回で一旦Laravel 7.x
の新機能につきましては最終回となります。
(実はまだ他にもありますが、小さなものなのでまた別の機会に、と考えています。ぜひご期待ください。m_ _m)
そして、今回紹介する内容ですが、実は1つの機能なのですが使い方が2つあるということで、少し複雑かもしれません。
ただ、使い方によってはとても便利なのでぜひ覚えていただけると嬉しいです。
その機能とは・・・
1.トークンを使ったAPIアクセス
2.SPAの認証機能
で、その名も「Laravel Sanctum」です。
【追記:2020.3.21】元々「Laravel Airlock」という名前でしたが、商標の問題があり変更されました。
ぜひ皆さんのお役に立てると嬉しいです😊✨
開発環境: Laravel 7.x
目次
前提として
通常のログイン機能をインストールし、テストユーザーを準備しておいてください。詳しくは、以下をご覧ください。
また、今回はアクセスに関連する機能なのでテストに便利なPostman
を使います。詳しくは、Postman をインストールをご覧ください。
パッケージのインストール&準備する
では、まずは以下のコマンドでSanctum
をインストールしましょう。
composer require laravel/sanctum
インストールが完了したら、以下のコマンドで必要なファイルをコピーします。
php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider"
これで、
- /database/migrations
- /conifg
にファイルが作成されました。
忘れずにマイグレーションも実行しておきましょう。
php artisan migrate
すると、personal_access_tokens
テーブルが作成されます。
そして、最後にUser
モデルにHasApiTokens
をセットしてトークンが使えるようにしたら準備は完了です!
<?php // 省略 use Laravel\Sanctum\HasApiTokens; class User extends Authenticatable { use Notifiable, HasApiTokens; // 省略 }
トークンを使った認証付きのAPI
Sanctum
を使って認証付きのAPIを作ってみます。
トークンを発行する
まずは、Sanctum
でアクセストークンをつくります。
以下のルートを追加してください。
/routes/web.php
Route::get('/create_token', function(){ $user = \App\User::find(1); $token = $user->createToken('my-api-token'); echo $token->plainTextToken; // トークンを表示 });
そして、ブラウザでアクセスするとトークンが表示され、personal_access_tokens
には以下のようなデータが追加されます。
※表示されているトークンとpersonal_access_tokens
に保存されるトークンの中身は違いますが問題ありません。よりセキュアするために暗号化されていると考えてください。
では、ブラウザ上に表示されたアクセス・トークンを使って実際にAPIにアクセスしてみましょう!
一番シンプルな使い方
まずはユーザー情報を取得するだけのシンプルな例を見てみましょう。
/routes/api.php
Route::middleware('auth:sanctum')->group(function(){ // ここは全て「sanctum」のミドルウェアが適用される Route::get('/user', function(Request $request){ return $request->user(); }); });
実はたったこれだけで、以下の機能が実装できます。
- トークンが正しければユーザー情報を返す
- それ以外は拒否(もしくはリダイレクト)する
では、テストとしてPostman
で「/api/user」にアクセスしてみましょう。
まずはトークン「なし」です。
想定通りページがリダイレクトしてアクセス拒否されました。(なお、Ajaxの場合、{"message":"Unauthenticated."}
が返ってきます)
では、「Authorization > Bearer Token」に先ほどのトークンを入力してアクセスしてみましょう。
はい!
今回はユーザー情報を取得することができました。
権限もつける
次にトークンの認証に加えて、特定の権限をつけてみましょう。
例えば、「他のユーザー情報を変更できる」権限です。
では、新しいトークンを発行しましょう。
/routes/web.php
Route::get('/create_token', function(){ $permissions = ['user:update']; // 権限 $user = \App\User::find(1); $token = $user->createToken('my-api-token', $permissions); echo $token->plainTextToken; // トークンを表示 });
ここで重要なのが、createToken()
の第2引数に権限を追加している部分です。
そして、personal_access_tokens
テーブルのabilities
には権限が追加されます。
では、tokenCan()
を通過できるかを見てみましょう。
/routes/api.php
Route::middleware('auth:sanctum')->group(function(){ // 省略 Route::put('/user', function(Request $request) { $user = $request->user(); if($user->tokenCan('user:update')) { return '権限あり!'; } return '権限なし...'; }); });
次のようになりました。
はい!
権限があると判断されました。
なお、第2引数を省略した場合abilities
が["*"]
となり、全権限があることになりますので注意してください。
トークンを取得する
ユーザーに与えられたトークンを取得するには、tokens
を使います。
$user = $request->user(); foreach($user->tokens as $token) { $name = $token->name; $abilities = $token->abilities; dd($name, $abilities); }
トークンを削除する
トークンの削除は、通常のDBデータ削除と同じです。
Route::delete('/user', function(Request $request){ $user = $request->user(); // 全てのトークンを削除 $user->tokens()->delete(); // 特定の権限があるトークンだけ削除 $user->tokens()->whereJsonContains('abilities', 'user:update')->delete(); });
SPAのログイン機能として使う
では、続いてSanctum
をSPA
(※1)のログイン機能として使う場合です。
※1:single-page application(シングルページ・アプリケーション)。簡単に言うとAjax
だけで画面移動をするサイトです。
準備する
まず、SPA
のログイン機能を使う場合は、EnsureFrontendRequestsAreStateful
というミドルウェアを有効にする必要があります。
/app/Http/Kernel.php
// 省略 use Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful; class Kernel extends HttpKernel { // 省略 protected $middlewareGroups = [ // 省略 'api' => [ EnsureFrontendRequestsAreStateful::class, // 👈ここ 'throttle:60,1', \Illuminate\Routing\Middleware\SubstituteBindings::class, ], ]; // 省略 }
また、SPA
のAjax通信にはaxios
を使うので、npm
でビルドしておきましょう。
まず、以下のコマンドでパッケージをインストールします。
npm install
そして、次はビルド(ファイルを1つにしたり省コードにしたりすること)ですが、初期状態では不要なものが含まれているのでコメントアウトしておきます。
/resources/js/app.js
// 省略 // 👇ここ // Vue.component('example-component', require('./components/ExampleComponent.vue').default); // 省略 // 👇ここ // const app = new Vue({ // el: '#app', // });
では、この状態でビルドします。
以下のコマンドを実行してください。
npm run dev
※もしくは、本番環境用の場合はこちら。(ただし時間がかかります)
npm run production
これで、/js/app.js
に必要なコードが集まりました。
【注意】なお、実行環境のドメインがlocalhost
ではない場合は、.env
に実際のドメインを追加してSanctum
が認識できるようにしておいてください。
SANCTUM_STATEFUL_DOMAINS=l7x.test
SPAページ(ログインフォーム)をつくる
続いて、SPA
で使うログインフォームをつくっていきます。
ルートをつくる
/routes/web.php
Route::get('/spa', function(){ return view('spa'); });
ビューをつくる
/resources/views/spa.blade.php
<html> <body> <div id="app"> <div v-if="!loggedIn"> ログインフォーム<br> <input type="email" v-model="email"> <br> <input type="password" v-model="password"> <br> <button type="button" @click="login">ログイン</button> </div> <div v-else> ログイン中!<br> <button type="button" @click="getUser">ユーザー情報を取得</button> </div> </div> <script src="/js/app.js"></script> <script> new Vue({ el: '#app', data: { loggedIn: false, email: '', password: '' }, methods: { login() { axios.get('/sanctum/csrf-cookie') .then(response => { const url = '/api/login'; const params = { email: this.email, password: this.password }; axios.post(url, params) .then(response => { this.loggedIn = response.data.result; }) .catch(error => { alert('ログインに失敗しました。'); }); }); }, getUser() { axios.get('/api/user') .then(response => { console.log(response.data); // ユーザー情報を取得 }); } } }); </script> </body> </html>
この中で重要なのは、login()
です。
このメソッドではまず、Sanctum
が用意してくれているURLにアクセスし、さらに/api/login
でログインすることになります。
※なお、この時点でCORS
に関連するエラーが表示される場合は、CORSの設定をするをご覧ください。
ブラウザで表示するとこうなります。
ログインする部分をつくる
では、実際にログインをする/api/login
をつくっていきましょう。
/routes/api.php
Route::post('/login', function(Request $request){ $credentials = $request->validate([ 'email' => 'required|email', 'password' => 'required' ]); if(auth()->attempt($credentials)) { return ['result' => true]; } return response(['message' => 'ユーザーが見つかりません。'], 422); });
この中では、送信データをチェックし、その後ログインを試みることになります。
実際にログインが成功するとこうなります。
ユーザー情報を取得する部分をつくる
では、最後にログインした状態でSanctum
のミドルウェアで守られたURLにアクセスし、自分のユーザーデータを取得してみましょう。
といっても、取得する先は、トークンの説明の時に作成したapi/user
です。
/routes/api.php
Route::middleware('auth:sanctum')->group(function(){ Route::get('/user', function(Request $request){ return $request->user(); }); });
この状態で「ユーザー情報を取得」ボタンをクリックすると以下のようにレスポンスがあります。
テストとして、クッキーを削除してボタンをクリックしてみましょう。
はい!
今度はアクセスが拒否されました。
成功です😊✨
おまけ:CORSの設定をする
実行環境によっては、クロスドメインの制約のせいでアクセスが拒否されることがあります。その場合は次の方法を試してみてください。
まず、axios
にデフォルト設定を追加します。
/resources/js/bootstrap.js
// 省略 window.axios = require('axios'); window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest'; window.axios.defaults.withCredentials = true; // 👈ここを追加 // 省略
npm run dev
でビルドします。(もちろんブラウザ側では、ctrl + F5
でキャッシュを無視するリロードを忘れないでください)
次に、corsの設定を変更します。
太字が変更した部分です。
/config/cors.php
<?php return [ // 省略 'paths' => ['api/*', 'sanctum/csrf-cookie'], // 👈ここ 'allowed_methods' => ['*'], 'allowed_origins' => ['*'], 'allowed_origins_patterns' => [], 'allowed_headers' => ['*'], 'exposed_headers' => false, 'max_age' => false, 'supports_credentials' => true, // 👈ここ ];
おわりに
・・・ということで、6回に渡ってLaravel 7.x
の新機能をご紹介してきましたがいかがだったでしょうか。
正直な気持ちでいうと、すでにLaravel 5.8
あたりで「もうこれ以上は便利にならないんじゃないだろうか」と思うほどいたれりつくせりでしたが、まだまだLaravel
は進化を続けているようです。
今後の機能にも期待が持てますね。
ではでは〜!