環境構築からWEBアプリ開発・スマホアプリ開発まで。ときには動画制作やゲームも。

supilog
すぴろぐ

ログインした人だけ動画が閲覧できるようにする(laravel9)

ログインした人だけ動画が閲覧できるようにする(laravel9)

少々前に動画を扱うことがありまして、ログインした人にだけ動画が閲覧できるようにする対応をしてみた。単純にログインしたら表示されるという対応ではなくて、ログインしていない状態でブラウザから動画のパスを入力しても表示されないようにするという意味です。そのときのメモです。

ちょっとゴリゴリやったので、いろいろと整理されてない点はごめんなさい。

対応内容

  • 動画再生ページに行くにはログインが必要
  • 動画再生ページには、<video>タグが挿入されており、srcには通常の動画パスが記載されている
  • srcの動画パスをコピーし、ブラウザに打ち込んだ際に、ログイン状態であれば閲覧可能で、未ログイン状態であれば閲覧不可能とする

対応方法

動画へのアクセスをWEBサーバーでさばかせずにPHP側に飛ばして、ログイン確認を行った後に動画レスポンスを返す。

構成

  • laravel9
  • nginx

※本対応は動画へのアクセスをPHPに飛ばすためにnginxに力を借りているので、ビルトインサーバー単体ではテストできません。筆者はdockerでチェックしています。

準備としてDBはsqlite、認証として「Laravel Breeze」を利用して簡易的なログイン構成を構築

ここは今回の説明であまり重要でないので、簡易的な構成。説明も省略します。それからテスト用に自作したmp4動画ファイルを用意しました。BGMはDOVA-SYNDROME様にお世話になっております。

https://dova-s.jp/

初手としてnginxで403を返却してみた

テストなので、直接的にバコーンと書いてみます。7〜9行目の部分を追加してみました。実際の運用ではもっと多くのファイルに対応しないといけないと思いますが、今は一旦これで。

server {
    listen       80;
    server_name  localhost;
    root   /var/www/html/public;
    index  index.php index.html;

    location /m/sample_movie.mp4 {
        return 403;
    }

    location / {
        try_files $uri $uri/ /index.php$is_args$args;
    }

    location ~ [^/]\.php(/|$) {
        fastcgi_pass php:9000;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME  $document_root$fastcgi_script_name;
        include       fastcgi_params;
    }
}

直接アクセスしてみます

動画への直接アクセスはブロックされた

しかし、この方法では、動画が再生されるべきログイン後のページでも再生ができない。そりゃあそうだ、自分でアクセス拒否してるんだから。

ということで動画へのアクセス方法を変えてみる。

動画をPHP側からレスポンスを返す

HTML

今は、下記のように、直接ファイルを取得しようとしている。

<video controls muted preload="none" oncontextmenu="return false;">
    <source src="/m/sample_movie.mp4" type="video/mp4">
</video>

例えば、それをこんな風に変えてみる。現状では動画1個だが、沢山の動画を配信するサイトをイメージして適当にパラメータを付けてみた

<video controls muted preload="none" oncontextmenu="return false;">
    <source src="/m/mp4?s=ebd9402a4728e025cfae25e601d2a485" type="video/mp4">
</video>

routes/web.php

コントローラーへ飛ばします

Route::group(['middleware' => ['web']], function () {
    // 動画取得
    Route::get('/m/mp4', [SamplesController::class, 'mp4'])->name('mp4');
});

Controller

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;

class SamplesController extends Controller
{
    public function mp4(Request $request)
    {
        // ログインしていない場合
        if (!Auth::check()) {
            abort(403);
        }

        // ログイン済みの場合は取得したパラメータから
        // 動画パスを取得してレスポンスを返す
        // →今回はテストなので、固定のパスとする
        $path = public_path('m/sample_movie.mp4');
        return response()->file($path);
    }
}

さて、どうなるでしょうか。

ログイン後の画面で再生することに成功しました。

確認

ファイル(/m/sample_movie.mp4)へ直接アクセスすると、nginxの403画面。

未ログイン状態で、パラメータ付きパス(/m/mp4?s=ebd9402a4728e025cfae25e601d2a485)へ直接アクセスすると、laravelの403画面。

ログイン状態でパラメータ付きパス(/m/mp4?s=ebd9402a4728e025cfae25e601d2a485)へ直接アクセスすると、動画再生可能。ログインしてさえいれば、直接アクセスすることは可能になっちゃうわけですね。ただこれは問題にならなそうなので、よしとします。

これで一応やりたかったことは出来た!

あとは403画面を適宜修正すればいい。nginx側は444ステータスを返却しちゃう手もあり。

まとめ

laravelでファイル自体のレスポンスを返却する処理をやったことがなかったので、初めての経験でした。

return response()->file($path);

もっと複雑になるかなと思っていたので、逆に驚いた。

皆様も、動画配信サービスとまではいかなくても、サービス内で動画を扱うような事もあるでしょうし、もしかしたら制限をかける要件に出くわすかもしれません。

その時は、laravelでやろうとしてた奴がいたなーと思い出してください(ぇ

それでは。