Basic認証を実装した備忘録として残します。
Basic認証について簡単に。。
・Basic認証にはBase64といエンコード方式が使用されている。
Base64: wiki
・Basic認証はセキュアな認証とは 「 言えない 」ため、簡易的なアクセス制限以外では使用しないこと。
Basic認証がなぜ安全とは言えないのか?
認証情報に特定のユーザー名とパスワードを設定しておき、入力された値が一致した場合に認証とします。
実装
Laravelの基本的なBasic認証の実装方法については、HTTP基本認証に記載されています。
今回はLaravelにデフォルトで用意されているauth.basic
ミドルウェアを使用せずに
独自のBasic認証用ミドルウェアを作成したいと思います。
まずはartisanコマンドでBasicAuthenticate
という名前のミドルウェアを作成します。
php artisan make:middleware BasicAuthenticate
app/Http/Middleware
に作成されたBasicAuthenticate.php
のコードです
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
class BasicAuthenticate
{
// ➀
public const AUTH_USER_NAME = 'hoge';
public const AUTH_PASSWORD = 'P@ssword';
public function handle(Request $request, Closure $next)
{
// ➁
if ($request->getUser() !== self::AUTH_USER_NAME || $request->getPassword() !== self::AUTH_PASSWORD) {
// ➂
abort(401, headers: [
header('WWW-Authenticate: Basic realm="Enter username and password."'),
header('Content-Type: text/plain; charset=utf-8')
]);
}
// ➃
return $next($request);
}
}
簡潔にするためコメントは省略しています。
コードの説明としましては
➀ まずクラス定数で認証情報の設定を行っています。
➁ メソッドインジェクションをしたRequestクラスの親クラス
Symfony\Component\HttpFoundation\Request
クラスが持つgetUser()
とgetPassword()
を使用し、グローバル変数$_SERVER
から
PHP_AUTH_USER
とPHP_AUTH_PASSWORD
というキーでそれぞれ入力された値を取得しています。
➂ 取得したユーザー名が設定したユーザー名もしくはパスワードと一致しない場合
abort()
メソッドで401(認証エラー)を返し、レスポンスheaderには入力フォームのポップアップを表示するための
WWW-Authenticate: Basic realm="message"
を入力しています。
➃ 最初は必ずif文に引っかかりポップアップが表示されます。
認証に成功した場合は、次のミドルウェアを実行します。
このreturn文をhandleメソッドに記述していない場合、もしくはif文の中で 記述している場合は
Attempt to read property "headers" on null
というエラーが発生してしまうので注意。
認証に成功した場合はリクエストヘッダーのAuthorizationに
認証したユーザー名とパスワードの組み合わせをbase64エンコードした文字列が挿入されます。(MDN)
Authorization: Basic Zm9vOlBAc3N3b3Jk
あとは作成したミドルウェアをカーネルに登録し、ルーティングに適用するだけです。
protected $routeMiddleware = [
'auth' => \App\Http\Middleware\Authenticate::class,
// 'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
'auth.basic' => \App\Http\Middleware\BasicAuthenticate::class, // 追加
// 以下省略
];
Route::middleware('auth.basic')->group(function(){
Route::get('/', function () {
return view('welcome');
});
});