Laravel护照,根据用户的角色添加范围

时间:2018-03-10 13:44:50

标签: laravel oauth-2.0 laravel-passport scopes laravel-5.6

我目前正在构建SPA类型的应用程序原型。第一步是使用Laravel Passport实现API并保护它。为此,我正在从现有结构中获取灵感:Laravel SPA。问题是,API URL都没有受到保护,这意味着,作为用户,我可以从API请求所有信息。

所以我决定从头开始,提高安全性。我正在使用角色和权限包:Laravel-permission

这是我第一次实现和API而且我坚持使用Laravel护照的范围概念,因为它们可以直接添加到API请求中,而无需根据用户的角色进行检查。

我找到了一个在StackOverflow上提供解决方案的人,可以在这里找到:Role based API url protection

所以这是我的实施:

// AuthServiceProvider
public function boot()
{
    $this->registerPolicies();

    // We define all the scopes for the tokens
    Passport::tokensCan([
        'manage-continents' => 'Manage continents scope',
        'read-only-continents' => 'Read only continents scope',
    ]);

    Passport::routes();
}

然后,我正在使用Laravel ressources controller创建一个REST控制器。

// Rest Controller

namespace App\Http\Controllers\API\GeoLocation;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;

use App\Models\GeoLocation\Continent as Continent;

class ContinentController extends Controller
{
    public function index()
    {
        // allow listing all continents only for token with manage continent scope
        return Continent::all();
    }

    public function store(Request $request)
    {
        // allow storing a newly created continent in storage for token with manage continent scope
    }


    public function show($id)
    {
        // allow displaying the continent for token with both manage and read only scope
    }
}

然后在api.php文件中,我添加了以下路由:

Route::get('/continents', 'API\GeoLocation\ContinentController@index')
    ->middleware(['auth:api', 'scopes:manage-continents']);

Route::post('/continents', 'API\GeoLocation\ContinentController@store')
    ->middleware(['auth:api', 'scopes:manage-continents']);

Route::get('/continents/{id}', 'API\GeoLocation\ContinentController@show')
    ->middleware(['auth:api', 'scopes:manage-continents, read-only-continents']);

然后我创建一个Controller来拦截请求,并根据用户角色添加范围。问题,我认为这种方法永远不会达到,因为我将在后面解释。

namespace App\Http\Controllers\Auth;

use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;

class ApiLoginController extends Controller
{
    use AuthenticatesUsers;

    protected function authenticated(Request $request, $user)
    {   
        // Implement your user role retrieval logic
        // for example retrieve from `roles` database table
        $roles = $user->getRoleNames();

        $request->request->add(['username' => $request->email]); 

        // @TODO to avoid many requests, we should just deepdive into
        // the collection returned by the role

        // grant scopes based on the role that we get previously
        if ($roles->contains('hyvefive_super_administrator')) 
        {
            // grant manage order scope for user with admin role
            $request->request->add([
                'scope' => 'manage-continents'
            ]);
        } 
        else 
        {
            // read-only order scope for other user role
            $request->request->add([
                'scope' => 'read-only-continents'
            ]);
        }

        // forward the request to the oauth token request endpoint
        $tokenRequest = Request::create(
            '/oauth/token',
            'post'
        );

        return Route::dispatch($tokenRequest);
    }
}

测试所有内容之前的最后一步是在api.php文件中添加该控制器的路由:

Route::post('login', 'Auth\ApiLoginController@login');

最后,为了测试,我只是使用Auth默认Laravel包中的HomeController,如下所示:

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use GuzzleHttp\Client;
use Auth;

class HomeController extends Controller
{
    /**
     * Create a new controller instance.
     *
     * @return void
     */
    public function __construct()
    {
        $this->middleware('auth');
    }

    /**
     * Show the application dashboard.
     *
     * @return \Illuminate\Http\Response
     */
    public function index(Request $request)
    {
        // from client application
        $http = new Client();

        $response = $http->post('http://hyvefivity.local/api/login', [
            'form_params' => [
                'grant_type'    => 'password',
                'client_id'     =>  4,
                'client_secret' => 'fsW4E5fcQC0TGeVHOrvr1qlZ8TEgrgpSRziVLCDS',
                'username'      => 'myemail@gmail.com',
                'password'      => 'my-secret-password',
                'scope'         => 'manage-continents',
            ],
        ]);

        // You'd typically save this payload in the session
        $auth = json_decode((string) $response->getBody(), true);

        var_dump($auth);

        /*$response = $http->get('http://hyvefivity.local/api/continents', [
            'headers' => [
                'Authorization' => 'Bearer '.$auth->access_token,
            ]
        ]);

        $continents = json_decode( (string) $response->getBody() );
*/
        // return view('home');
    }
}

问题在于我觉得永远不会到达ApiLoginController,对于经过身份验证的方法也是如此。

如果我正在做以下事情:

$http = new Client();

$response = $http->post('http://hyvefivity.local/oauth/token', [
    'form_params' => [
        'grant_type'    => 'password',
        'client_id'     =>  4,
        'client_secret' => 'fsW4E5fcQC0TGeVHOrvr1qlZ8TEgrgpSRziVLCDS',
        'username'      => 'my-email@gmail.com',
        'password'      => 'my-seret-password',
        'scope'         => 'manage-continents',
    ],
]);

生成令牌,但不是我在ApiLoginController中添加的作用域。

另外我想要做的另一项改进是:我应该在登录期间进行API调用,因为如果我在HomeController中执行此操作,问题是密码被哈希,这意味着无法询问在登录期间没有密码的密码授予类型的令牌?

1 个答案:

答案 0 :(得分:-1)

护照默认提供,但也无法正常工作

Passport::actingAs($user,['scope']);



public static function actingAs($user, $scopes = [], $guard = 'api')