【Laravel】画像の直アクセスを禁止して、特定の条件を突破した場合のみ画像を表示する方法

こんにちは!Hajimariの新卒エンジニアの稲葉です。
2020年4月1日に新卒エンジニア2期生として入社しました!

普段は、自社プロダクトであるスタートアップ向けマッチングサイト構築パッケージPIECE(https://crowd.itpropartners.com/piece/)の開発や受託開発を行っています!

今回はログインしていない場合に画像の直アクセスを禁止する方法について書いていきます。
htaccessで直アクセスを制限する方法も考えられますが、ログインしている場合は直アクセスの許可するため、Laravel、Nginxで実装していきます。

直アクセス

storage/files配下に設置した画像が、 /storage/files/画像名でアクセスした際に画像が表示されます。
特定の条件を突破した場合のみ表示させたい画像がある場合は、直アクセスを禁止してみてください。
万が一直アクセスをされた際に、画像が表示されるの防ぐことができます。

実装

まずはstorage配下にimagesディレクトリを作成。
今回直リンクを禁止する画像をstorage/images配下に設置します。

画像の設置後、Nginxでlocationの設定をします。 /imagesにアクセスした際、内部リダイレクトされます。

location /images {
    try_files /index.php?$uri /index.php?$query_string;
}


次にルーティングの定義です。
whereメソッドはパラメーターのフォーマットを制約します。
下記ではパラメーター(path)を0文字以上の任意の文字列に制約。

Route::get('/images/{path?}', 'FileAccessController@index')->where(['path' => '.*']);


ルーティングの定義後、traitの作成です。
traitの作成後は必要なメソッドを定義していきます。
作成したtraitは任意のクラスで継承すれば、継承したtraitに定義されているメソッドをそのクラスが利用することができます。

use Illuminate\Support\Facades\Storage;

Trait ImageTrait
{
  public function getDiskInstance()
   {
       return Storage::disk('local');
   }

  public function storageExist($fileName)
   {
       return $this->getDiskInstance()->exists($fileName);
   }
}

getDiskInstance()メソッドでは、Storageファサードのdiskメソッドを使用してディスクインスタンスの取得。
storageExist()メソッドでは、existsメソッドを使用してファイルが存在しているか判定します。
存在する場合はtrueを返します。


trait作成後、filesystems.php で local のベースディレクトリがどこになるかを指定します。
今回はstorage/images配下に画像を設置したため、imagesディレクトリを指定。
デフォルトではappディレクトリが指定されています。

'disks' => [

        'local' => [
            'driver' => 'local',
            // 変更前
            // 'root' => storage_path('app'),
            'root' => storage_path('images'),
        ],


最後にコントローラの作成、indexメソッドを定義をします。
先ほど作成したtraitを下記で継承することが可能です。

use トレイト名;

ログインしている場合のみ直リンクを許可します。

class FileAccessController extends Controller
{
    use ImageTrait;

    public function index($path=null, Request $request)
    {
        $exist = $this->storageExist($path);

        if (!$exist) {
            abort(404);
        }

        if(Auth::check()){
            return $this->getDiskInstance()->response($path);
        }else {
            abort(404);
        }
        
    }
}

実装は以上です。


現在は家族の事情もあり、リモートワークを行っています。
内定者インターンから数えると約10ヶ月リモートワークを継続中です。
今後は長期間のリモートワークについても書いていこうと思います。


最後に

Hajimariでは、Laravel、vue.js、Nuxt.jsで開発に挑戦したいエンジニア・デザイナーを絶賛募集中です!

そして、22卒新卒エンジニアのエントリーも心よりお待ちしております!

www.wantedly.com

www.wantedly.com