前回は、画像をパブリックに表示する方法を説明しましたが、今回は画像をプライベートに表示する方法です。
いくつか方法があります。
まず、前回のようにアップロードをパブリックの場所に保存して、特定のユーザーだけに表示のためのURLを教える。
しかし、DBから自動発行されるproduct_image_id
を画像ファイル名に使用するなら、URLを操作することで他のファイルも見れてしまいます。
そうなら、画像のURLをわかりにくいように変えて、他の画像のURLを予想しにくくすることも可能です。
例えば、235.jpg
とは見せずに、1f3870be274f6c49b3e31a0c6728957f.jpg
にするとか。
それは、md5()を利用することで簡単に可能です。
public function filename() { $ext = 'jpg' ; switch ( $this ->mime) { case 'image/jpeg' : case 'image/jpg' : $ext = "jpg" ; break ; case 'image/png' : $ext = "png" ; break ; case 'image/gif' : $ext = "gif" ; break ; } return sprintf( "%d.%s" , md5( $this ->product_image_id), $ext ); } |
よりセキュアにするには、DBに保存するときに、uniqid()あるいは、openssl-random-pseudo-bytes()を使用してランダムな値を生成して、その値をファイル名として保存するとか。要するに、IDのように連続な番号とはならないので、容易にファイル名を予測できないようにすることです。
しかし、究極は、ファイルをパブリックから見れない場所に保存して、それを表示する方法です。
例えば、storage/images/product/1.jpg
のように、パブリックから見れないstorage
のディレクトリに画像をアップロードするようにして、見せるときには、ログインしたユーザーと関連ある画像だけを、そのユーザーに表示する。
この場合は、パブリックに保存されている画像と違い、固定のURLを通してウェブサーバーに画像の表示を任せることはできません。逆に、あたかもウェブサーバーが画像ファイルを読んでデータをストリームするという作業と同じことをプログラムで行います。header()
を使用すれば、そう難しいことではありません。
namespace App\Http\Controllers\User; use Illuminate\Http\Request; use Log; use App\Http\Requests; use App\Http\Controllers\Controller; use App\Product; use App\ProductImage; class ProductController extends Controller { public function getImage(Product $product ) { return view( 'user/product_image' , compact( 'product' )); } public function downloadImage(ProductImage $product_image ) { //@TODO ここで、認証したユーザーに画像を表示していいかどうかをチェック。 //そうでないなら、空の画像を表示 $filename = $product_image ->filename(); header( "Content-type: $product_image->mime name=$filename" ); header( "Content-Disposition: attachment; filename=$filename" ); header( "Content-Length: " .@ filesize ( $product_image ->path)); header( "Expires: 0" ); @readfile( $product_image ->path); exit ; } } |
上で使用されるテンプレートは、
@ extends ( 'user.layouts.app' ) @section( 'content' ) <div class = "container" > <div class = "row" > <div class = "col-md-8 col-md-offset-2" > <div class = "panel panel-default" > <div class = "panel-heading" >アップロードしたファイルを表示</div> <div class = "panel-body" > <div> @ foreach ( $product ->product_images as $image ) <img src= "{{ url('/user/product_image', $image->product_image_id) }}" > @ endforeach </div> </div> </div> </div> </div> </div> @endsection |
のようになります。
routes.php
は、以下のように認証保護された中でコールされます。
Route::group([ 'prefix' => 'user' , 'middleware' => 'web' ], function () { Route::get( 'login' , 'User\Auth\AuthController@showLoginForm' ); Route::post( 'login' , 'User\Auth\AuthController@login' ); Route::get( 'logout' , 'User\Auth\AuthController@logout' ); .. Route::group([ 'middleware' => 'auth:user' ], function () { Route::get( 'home' , 'User\HomeController@index' ); .. Route::get( 'product/{product}/image' , 'User\ProductController@getImage' ); Route::get( 'product_image/{product_image}' , 'User\ProductController@downloadImage' ); }); }); |