Laravel Passport Scopes

Jer*_*zeg 31 scopes oauth-2.0 laravel laravel-5.3 laravel-passport

我对laravel范围部分有点困惑.

我有一个用户模型和表.

如何为用户分配用户,客户和/或管理员的角色.

我有一个带vue和laravel api后端的SPA.我使用https://laravel.com/docs/5.3/passport#consuming-your-api-with-javascript

    Passport::tokensCan([
        'user' => 'User',
        'customer' => 'Customer',
        'admin' => 'Admin',
    ]);
Run Code Online (Sandbox Code Playgroud)

如何指定哪个用户模型具有哪个范围?

或者范围与角色不同?

你会如何实现这个?

提前致谢!

Ray*_*nda 66

或者范围与角色不同?

两者之间最大的区别在于它们适用的背景.基于角色的访问控制(RBAC)在直接使用Web应用程序时控制用户的访问控制,而Oauth-2范围则代表用户管理对外部客户端的API资源的访问.

如何指定哪个用户模型具有哪个范围?

在一般的Oauth流程中,请求用户(作为资源所有者)授权客户端代表他/她可以做什么和不能做什么,这些就是你所谓的范围.在成功授权时,客户端请求的范围将被分配给生成的令牌而不是用户本身.

根据您选择的Oauth授权流程,客户端应在其请求中包含范围.在授权代码授予流程中,当将用户重定向到授权页面时,范围应包含在HTTP GET查询参数中,而在密码授予流程上,范围必须包含在HTTP POST正文参数中以请求令牌.

你会如何实现这个?

这是密码授权流程的示例,假设您事先已完成laravel/passport设置

定义管理员和用户角色的范围.尽可能具体,例如:admin可以管理订单,用户只能读取它.

// in AuthServiceProvider boot
Passport::tokensCan([
    'manage-order' => 'Manage order scope'
    'read-only-order' => 'Read only order scope'
]);
Run Code Online (Sandbox Code Playgroud)

准备REST控制器

// in controller
namespace App\Http\Controllers;

class OrderController extends Controller
{   
    public function index(Request $request)
    {
        // allow listing all order only for token with manage order scope
    }

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

    public function show($id)
    {
        // allow displaying the order for token with both manage and read only scope
    }
}
Run Code Online (Sandbox Code Playgroud)

使用api guard和scope分配路由

// in api.php
Route::get('/api/orders', 'OrderController@index')
    ->middleware(['auth:api', 'scopes:manage-order']);
Route::post('/api/orders', 'OrderController@store')
    ->middleware(['auth:api', 'scopes:manage-order']);
Route::get('/api/orders/{id}', 'OrderController@show')
    ->middleware(['auth:api', 'scopes:manage-order, read-only-order']);
Run Code Online (Sandbox Code Playgroud)

发布令牌时,首先检查用户角色,然后根据该角色授予范围.为此,我们需要一个额外的控制器,它使用AuthenticatesUsers特性来提供登录端点.

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
        $role = $user->checkRole();

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

        // forward the request to the oauth token request endpoint
        $tokenRequest = Request::create(
            '/oauth/token',
            'post'
        );
        return Route::dispatch($tokenRequest);
    }
}
Run Code Online (Sandbox Code Playgroud)

添加api登录端点的路由

//in api.php
Route::group('namespace' => 'Auth', function () {
    Route::post('login', 'ApiLoginController@login');
});
Run Code Online (Sandbox Code Playgroud)

而不是POST到/ oauth/token路由,POST到我们之前提供的api登录端点

// from client application
$http = new GuzzleHttp\Client;

$response = $http->post('http://your-app.com/api/login', [
    'form_params' => [
        'grant_type' => 'password',
        'client_id' => 'client-id',
        'client_secret' => 'client-secret',
        'username' => 'user@email.com',
        'password' => 'my-password',
    ],
]);

return json_decode((string) $response->getBody(), true);
Run Code Online (Sandbox Code Playgroud)

成功授权后,将为客户端应用程序发出基于我们之前定义的范围的access_token和refresh_token.保留该位置,并在向API发出请求时将令牌包含在HTTP标头中.

// from client application
$response = $client->request('GET', '/api/my/index', [
    'headers' => [
        'Accept' => 'application/json',
        'Authorization' => 'Bearer '.$accessToken,
    ],
]);
Run Code Online (Sandbox Code Playgroud)

API现在应该返回

{"error":"unauthenticated"}
Run Code Online (Sandbox Code Playgroud)

每当具有under privilege的令牌用于使用受限制的端点时.

  • 这篇文章非常有帮助.非常感谢.我必须以不同的方式做一件事,我在Laravel 5.3上并且在任何地方都没有"AuthenticatorService.php".我根据Laravel文档在App\Providers\AuthServiceProvider中使用了启动功能. (3认同)

Leo*_*gui 5

实施 Raymond Lagonda 响应,效果非常好,只是要小心以下几点。您需要重写 ApiLoginController 中 AuthenticatesUsers 特征中的一些方法:

    /**
     * Send the response after the user was authenticated.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    protected function sendLoginResponse(Request $request)
    {
        // $request->session()->regenerate(); // coment this becose api routes with passport failed here.

        $this->clearLoginAttempts($request);

        return $this->authenticated($request, $this->guard()->user())
                ?: response()->json(["status"=>"error", "message"=>"Some error for failes authenticated method"]);

    }

    /**
     * Get the failed login response instance.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\RedirectResponse
     */
    protected function sendFailedLoginResponse(Request $request)
    {
        return response()->json([
                                "status"=>"error", 
                                "message"=>"Autentication Error", 
                                "data"=>[
                                    "errors"=>[
                                        $this->username() => Lang::get('auth.failed'),
                                    ]
                                ]
                            ]);
    }
Run Code Online (Sandbox Code Playgroud)

如果您将登录:用户名字段更改为自定义用户名字段,例如:e_mail。您必须像 LoginController 中那样完善用户名方法。此外,您还必须重新定义和编辑方法:validateLogin、attemptLogin、credentials,因为一旦验证登录,请求就会转发到passport,并且必须称为用户名。