Access files in storage folder only through Auth M

2019-03-29 08:31发布

问题:

I have following folder in my Laravel website.

/storage/Asset/Media

This folder can have info like below

/storage/Asset/Media/1/abc.png

/storage/Asset/Media/2/abc.png

Here 1 or 2 is the folder names.

I have following code to secure the folder so that nobody can access the folder without authentication

Route::group(['middleware' => ['web', 'auth']], function () {
    Route::get('/storage/Asset/Media/{ID}/{eded}', array(
        'as' => 'Files',
        'uses' => 'User\Account\Media\MediaController@DownloadMedia',
    ));
});

so in this way nobody can access the files until user's session is not expired in a browser.

Issue is in Android, so now nobody can access the files due to Auth Middleware.

Can somebody suggest the approach such that, files can be accessible to download via Token Based Authentication(through Android) and also using Auth Controller(through Website)?

回答1:

You don't need to use any other config in routes.php, everything will work just fine if You follow this guide:

The easiest solution would be to create column named api_token is users table. Then when trying to access resource from android device, just add ?api_token=<token> to Your URL, where <token> is a api_token column in Your users table.

For example: domain.com/storage/Asset/Media/1/2?api_token=123hello4secret

System will try to search for user record with api_token == 123hello4secret, so just put that 123hello4secret into Your user api_token field.


If You wonder why You should api_token as column name, the answer is here: https://github.com/laravel/framework/blob/2a38acf7ee2882d831a3b9a1361a710e70ffa31e/src/Illuminate/Auth/TokenGuard.php#L45 Laravel will try to authorize You using api_token if it is found in request fields.


Also You can use HTTP headers to authorize with token:
Header example:

Authorization: Bearer 123hello4secret


回答2:

Note that by default 'storage' folder is not accessible anyway - only 'public' folder is publicly accessible. Therefore, you can use any URLs for file download controller.

You need two routes - one for cookie-based authentications (web users) and one for stateless (token) sessions:

// URL can be anything you want
Route::get('downloads/{ID}/{eded}', [
        'middleware' => ['web', 'auth'],
        'as' => 'Files',
        'uses' => 'MediaController@DownloadMedia',
]); 

// For API tokens, use 'api' guard of Auth middleware
// URL has to be different, too
Route::get('api/downloads/{ID}/{eded}', [
        'middleware' => ['auth:api'],
        'as' => 'Files',
        'uses' => 'MediaController@DownloadMedia',
]); 

You also need to add a token field to your users table. Add a line to your users table migration:

$table->string('api_token', 60)->unique();

Then, you need to generate this token. If you want to use the same token (no expiration or refresh), you could generate it when a new user is being created. You could have this in your User class:

/**
 * @return void
 */
public function boot()
{
    User::creating(function ($user) {
        $user->api_token = str_random(60);
    });
}

Otherwise, you could force the token to be refreshed on every login attempt (postLogin in AuthController) or maybe have a separate controller method that exchanges email and password for a token.



回答3:

You will need to wrap a new Route within an auth:api Middleware

This is very simple using Laravel's API authentication.



回答4:

you can put .htaccess file in /storage/Asset/Media

with code:

<IfModule mod_rewrite.c>
    <IfModule mod_negotiation.c>
        Options -MultiViews
    </IfModule>

    RewriteEngine On

    # Redirect Trailing Slashes If Not A Folder...
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteRule ^(.*)/$ /$1 [L,R=301]

    # Handle Front Controller...
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteRule ^ index.php [L]

    # Handle Authorization Header
    RewriteCond %{HTTP:Authorization} .
    RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
</IfModule>

you can access files in php but direct link to file will not work.



回答5:

In your users table make a nullable column api_token

Now every time user login via the Api, set this api_token in db and send this to the Api consumer.

Now on further request the Api consumer will send back the token in request header. So just configure the Authenticate Middleware:

    public function handle($request, Closure $next)
    {   
      if(!empty($request->header('Auth-Token'))) {
        $api_token = $request->header('Auth-Token');
        $user = User::where('api_token', $api_token)->first();
        if(is_null($user)) //Unauthorize: write response to return 
      } elseif ($this->auth->guest()) {
        if ($request->ajax()) {
            return response('Unauthorized.', 401);
        } else {
            return redirect()->guest('/');
        }
     }

      return $next($request);
    }