概要
LaravelのAPIを実装し、次のようなことを調査・実装し、Postmanのあまりの便利さに感動したのでまとめる。
これがベスト、ではなくこう実装した、という記録。
- ログイン中のユーザだけが利用できるようにする方法。
- 実装したAPIのうち、ユーザ情報を返すAPIがあるため。未ログインのユーザには見せたくない。
- APIは、モーダル上に情報を表示したり、取得した情報をセレクトボックスの選択肢として表示するのに使われる。
- APIの動作確認のため、PostmanでCookie付きでリクエストする方法。
- ログインしていれば200レスポンスが返ること、ログアウト状態なら情報が取得できないことを確かめたい。
- (おまけ)実際にjsでajax通信する際は、どのように認証させればいいのか。
動作確認環境
Laravel 6.20.2
PHP 7.4
ログイン中のユーザだけが利用できるように、セッションIDで認証する
今回の開発においてはAPIトークンやLaravel Passportを使った実装を行う予定がないため、Laravelのデフォルトで用意されているapi
ミドルウェアグループの定義を書き換える。
ミドルウェアの追加
Laravelのデフォルトのままでは route/api.php
に記述されたルーティングにはCookieとセッションによる認証に関するミドルウェアが設定されていない。
APIに対してCookieとセッションによる認証を有効化するため、Kernel.php
にミドルウェアを4点追加する。
app/Http/Kernel.php
protected$middlewareGroups=['web'=>[\App\Http\Middleware\EncryptCookies::class,\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,\Illuminate\Session\Middleware\StartSession::class,// \Illuminate\Session\Middleware\AuthenticateSession::class,\Illuminate\View\Middleware\ShareErrorsFromSession::class,\App\Http\Middleware\VerifyCsrfToken::class,\Illuminate\Routing\Middleware\SubstituteBindings::class,],'api'=>[★★★ここから★★★\App\Http\Middleware\EncryptCookies::class,\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,\Illuminate\Session\Middleware\StartSession::class,\Illuminate\View\Middleware\ShareErrorsFromSession::class,★★★ここまで★★★'throttle:60,1',\Illuminate\Routing\Middleware\SubstituteBindings::class,],];
APIのルーティングをauth
ミドルウェアを利用するように修正する
route/api.php
には、デフォルトだと次のようにただログイン中ユーザの情報を返すAPIが記述されている。
ここではauth:api
というように、api guardが利用されている。
Route::middleware('auth:api')->get('/user',function(Request$request){return$request->user();});
これを、auth
に書き換える。
Route::middleware('auth')->group(function(){// ルーティングも実装したAPIに書き換えるRoute::get('/users',xxxxxx);});
他、試したこと
当初、config/auth.php
のapi guardの部分を、
'guards'=>['web'=>['driver'=>'session','provider'=>'users',],'api'=>['driver'=>'token','provider'=>'users','hash'=>false,],],
下記のように、 auth.phpで定義したguardをroute/api.php
でミドルウェアに設定すればいいと思ったが、そうしても認証できなかった。
(SessionDriverが生成されること、Illuminate\Auth\GuardHelper の
authenticate()で$this->user()が空になっていることまでは突き止めた。なのでキャッシュが残っていることが原因ではない。
src/Illuminate/Auth/SessionGuard.phpのuser()のなかでユーザが取得できていないっぽい。でも、driverとproviderを同じものにしているのにミドルウェアをauth:api
→auth
にすると認証できる理由がわからない・・・。)
'api'=>['driver'=>'session','provider'=>'users',],
結局、config/auth.php
は書き換えず、ミドルウェアをauth:api
からauth
に書き換えることにした。
何か分かれば追記する。
参考
- Laravelで「画面ログインしてるときだけ叩けるAPI」を実装した際の備忘録
- LaravelでセッションIDで認証状態をチェックするステートフルなAPIを使う
- CookieによるAPI経由のユーザー認証機能を作る【Laravel6とNuxt.jsで作る管理画面】
- Laravelのmiddleware authを理解したい(6.x)
PostmanでCookie付きでリクエストする
ログインしているユーザのみ利用できるように、Cookieとセッションを使った認証を行うようにしたため、PostmanでAPIの動作確認をするには、Cookie付きでリクエストする必要がある。
以下の手順で設定を行う。
Google拡張機能 Postman Interceptorをインストールする。
「Capture Cookies」をONにし、動作確認をしたい環境のドメインを追加する。
localの場合はlocalhost
で、ポート番号は不要。Postman側でも、Interceptor Bridgeをインストールする。
(Capture requests and cookies with Postman
のアイコンから。初回のみ)「Capture Cookies」をONにし、動作確認をしたい環境のドメインを追加する。
localの場合はlocalhost
で、ポート番号は不要。以上の手順でクッキーがキャプチャーされている状態になる。
ブラウザでlocalhostにアクセスし、Postman側でCookies(下記画像参照)にクッキーが含まれていることを確認する。 (手動で書き換えることも可能と思われるが、勝手にcaptureしてくれるので使う場面がなかった。)
画面上でログインした状態でAPIを正常なリクエストすると200レスポンスが返り、ログアウト状態でリクエストすると 401 Unauthorize が返ることを確認できればOK。
ログインしているはずなのに401が返ってくる、というときは、Cookieをcaptureできているかどうか確認すること。
参考
「Postman Intercepter」Cookie利用時のREST用ツール
- 4年前の記事なのでインターフェースなどが異なるものの、 Postman側もInterceptorをONにする必要があるなど、使い方がざっくり分かったので助かりました。
その他注意事項
ブラウザ側のInterceptorとPostmanがDISCONNECT状態になることがあったが、ブラウザまたはPostmanを再起動すれば正常に接続できた。
(参考)ajax通信する際、同一ドメインに対してはCookieを送信するようになっている
実装したAPIを画面側で呼び出す際は、Cookie情報は一切触れていない。
なぜなら、呼び出す側と呼び出すAPIが同一ドメインである場合、Cookieは勝手に送信されるようになっているから。
ajaxで呼び出す際の実際のコードはこんな感じ。
ログインさえしていれば、呼び出すことができる。
$.ajax({url:'/api/xxxxx/',type:'GET',contentType:'application/json; charset=UTF-8',data:{// 「type: 'GET'」と指定しているので、自動でGETパラメータとして扱われる}}).then((res)=>{// モーダルに表示したり、セレクトボックスの選択肢として追加したり、etc...});
参考
jqueryの.ajax()メソッドがセッションCookieを送信しないのはなぜですか?
- このサイトを参考にしたが、あまり信憑性がないので参考まで。
- 『Webを支える技術』『安全なWebアプリケーションの作り方』も確認したが、『Webを支える技術』には明確な記述はなかった。 『安全なWebアプリケーションの作り方』p.65 に、
いったんクッキー値を覚えたブラウザは、
その後同じサイト(example.jp)にリクエストを送信する際には、覚えたクッキー値(PHPSESSID=・・・)を送信します。と記載があった。
javascriptでリクエストしようがPHPでリクエストしようが対象が同一ドメインなら、ブラウザはCookieを送信するようになっている、と捉えることにした。