Laravel 护照范围
Laravel Passport Scopes
我对 laravel 范围部分有点困惑。
我有一个用户模型和 table。
如何为用户分配用户角色,客户 and/or 管理员。
我有一个带有 vue 和 laravel api 后端的 SPA。我用 https://laravel.com/docs/5.3/passport#consuming-your-api-with-javascript
Passport::tokensCan([
'user' => 'User',
'customer' => 'Customer',
'admin' => 'Admin',
]);
我如何分配哪个用户模型具有哪个范围?
或者范围与角色不同?
您将如何实施?
提前致谢!
Or are scopes not the same as roles?
两者最大的区别在于它们适用的上下文。 Role-based 访问控制 (RBAC) 管理用户在 直接 使用 Web 应用程序时的访问控制,而 Oauth-2 范围管理对 API 资源的访问代表用户的 external client。
How can i assign which user model has which scope(s)?
在一般的 Oauth 流程中,要求用户(作为资源所有者)授权客户端代表 his/her 可以做和不能做的事情,这些就是你所说的 范围。 成功授权客户端请求的范围将是assigned to the generated token而不是用户本身。
根据您选择的 Oauth 授权流程,客户端应在其请求中包含范围。在授权代码授权流程中,当将用户重定向到授权页面时,范围应包含在 HTTP GET 查询参数中,而在密码授权流程中,范围必须包含在 HTTP POST body 参数中以请求令牌.
How would you implement this?
这是密码授予流程的示例,假设您事先完成了 laravel/passport 设置
定义管理员和用户角色的范围。尽可能具体,例如:管理员可以 manage-order 而用户只能阅读。
// in AuthServiceProvider boot
Passport::tokensCan([
'manage-order' => 'Manage order scope'
'read-only-order' => 'Read only order scope'
]);
准备 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
}
}
为路由分配 api 守卫和范围
// 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']);
并且在颁发令牌时首先检查用户角色并根据该角色授予范围。为此,我们需要一个额外的控制器,它使用 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);
}
}
为 api 登录端点添加路由
//in api.php
Route::group('namespace' => 'Auth', function () {
Route::post('login', 'ApiLoginController@login');
});
而不是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);
授权成功后,将根据我们之前定义的范围为客户端应用程序发出一个access_token和一个refresh_token。将其保存在某处,并在向 API.
发出请求时将令牌包含到 HTTP header
// from client application
$response = $client->request('GET', '/api/my/index', [
'headers' => [
'Accept' => 'application/json',
'Authorization' => 'Bearer '.$accessToken,
],
]);
API现在应该return
{"error":"unauthenticated"}
每当使用权限不足的令牌来使用受限端点时。
实现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'),
]
]
]);
}
如果您将登录:用户名字段更改为自定义用户名字段,例如:e_mail。您必须像在您的 LoginController 中一样改进用户名方法。
此外,您还必须重新定义和编辑方法:validateLogin、attemptLogin、credentials,因为一旦登录被验证,请求就会被转发到 passport 并且必须被称为 username。
我知道这有点晚了,但是如果您在 SPA 中使用 Web 中间件中的 CreateFreshApiToken
使用后端 API,那么您只需添加一个 'admin' 中间件到您的应用程序:
php artisan make:middleware Admin
然后在 \App\Http\Middleware\Admin
中执行以下操作:
public function handle($request, Closure $next)
{
if (Auth::user()->role() !== 'admin') {
return response(json_encode(['error' => 'Unauthorised']), 401)
->header('Content-Type', 'text/json');
}
return $next($request);
}
确保您已将 role
方法添加到 \App\User
以检索用户角色。
现在您需要做的就是在 app\Http\Kernel.php
$routeMiddleware
中注册您的中间件,如下所示:
protected $routeMiddleware = [
// Other Middleware
'admin' => \App\Http\Middleware\Admin::class,
];
并将其添加到您在 routes/api.php
中的路线
Route::middleware(['auth:api','admin'])->get('/customers','Api\CustomersController@index');
现在,如果您尝试在未经许可的情况下访问 api,您将收到“401 未经授权”错误,您可以在您的应用中检查并处理该错误。
我已经成功地使用@RaymondLagonda 解决方案,使用 Laravel 5.5 和 Sentinel,但是它应该,也可以,没有哨兵。
该解决方案需要覆盖一些 class 方法(因此请记住这一点,以便将来更新),并为您的 api 路由添加一些保护(不公开 client_secret例如)。
第一步,是修改您的 ApiLoginController
以添加构造函数:
public function __construct(Request $request){
$oauth_client_id = env('PASSPORT_CLIENT_ID');
$oauth_client = OauthClients::findOrFail($oauth_client_id);
$request->request->add([
'email' => $request->username,
'client_id' => $oauth_client_id,
'client_secret' => $oauth_client->secret]);
}
在此示例中,您需要在 .env 中定义 var ('PASSPORT_CLIENT_ID') 并创建 OauthClients 模型,但您可以通过在此处放置适当的测试值来安全地跳过此步骤。
需要注意的一件事是,我们将 $request->email
值设置为用户名,只是为了遵守 Oauth2 约定。
第二步是覆盖 sendLoginResponse
方法,该方法会导致 Session storage not set
等错误,我们在这里不需要会话,因为它是 api.
protected function sendLoginResponse(Request $request)
{
// $request->session()->regenerate();
$this->clearLoginAttempts($request);
return $this->authenticated($request, $this->guard()->user())
?: redirect()->intended($this->redirectPath());
}
第三步是,按照@RaymondLagonda 的建议修改经过身份验证的方法。你需要在这里写你自己的逻辑,特别是配置你的作用域。
最后一步(如果您使用的是 Sentinel)是修改 AuthServiceProvider
。添加
$this->app->rebinding('request', function ($app, $request) {
$request->setUserResolver(function () use ($app) {
return \Auth::user();
// return $app['sentinel']->getUser();
});
});
在引导方法中 $this->registerPolicies();
之后。
完成这些步骤后,您应该能够通过提供用户名 ('this will always be email, in this implementation')、密码和 grant_type='password'[=21 让您的 api 正常工作=]
此时,您可以添加中间件范围 scopes:...
或 scope:...
来保护您的路由。
我希望它真的能帮到你...
使用@RaymondLagonda 解决方案。如果您收到 class scopes not found 错误,请将以下中间件添加到 app/Http/Kernel.php
文件的 $routeMiddleware
属性 中:
'scopes' => \Laravel\Passport\Http\Middleware\CheckScopes::class,
'scope' => \Laravel\Passport\Http\Middleware\CheckForAnyScope::class,`
此外,如果您收到错误 Type error: Too few arguments to function
,您应该能够从如下请求中获取 $user
。
(我正在使用 laratrust 管理角色)
public function login(Request $request)
{
$email = $request->input('username');
$user = User::where('email','=',$email)->first();
if($user && $user->hasRole('admin')){
$request->request->add([
'scope' => 'manage-everything'
]);
}else{
return response()->json(['message' => 'Unauthorized'],403);
}
$tokenRequest = Request::create(
'/oauth/token',
'post'
);
return Route::dispatch($tokenRequest);
}
谢谢你,这个问题在我脑海里萦绕了一段时间!我使用 Raymond Lagonda 的解决方案为 Laravel 5.6 定制了一点,使用内置速率限制,使用单个 thirdparty
客户端(或者如果需要的话可以更自定义),同时仍然给每个用户一个列表权限(范围)。
- 使用 Laravel Passport
password
授予并遵循 Oauth 流程
- 使您能够为不同的用户设置角色(范围)
- 不要 expose/release 客户端 ID 或客户端密码,只有用户的用户名(电子邮件)和密码,几乎是 password 授权,减去 client/grant 东西
示例在底部
routes/api.php
Route::group(['namespace' => 'ThirdParty', 'prefix' => 'thirdparty'], function () {
Route::post('login', 'ApiLoginController@login');
});
ThirdParty/ApiLoginController.php
<?php
namespace App\Http\Controllers\ThirdParty;
use Hash;
use App\User;
use App\ThirdParty;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
class ApiLoginController extends Controller
{
use AuthenticatesUsers;
/**
* Thirdparty login method to handle different
* clients logging in for different reasons,
* we assign each third party user scopes
* to assign to their token, so they
* can perform different API tasks
* with the same token.
*
* @param Request $request
* @return Illuminate\Http\Response
*/
protected function login(Request $request)
{
if ($this->hasTooManyLoginAttempts($request)) {
$this->fireLockoutEvent($request);
return $this->sendLockoutResponse($request);
}
$user = $this->validateUserLogin($request);
$client = ThirdParty::where(['id' => config('thirdparties.client_id')])->first();
$request->request->add([
'scope' => $user->scopes,
'grant_type' => 'password',
'client_id' => $client->id,
'client_secret' => $client->secret
]);
return Route::dispatch(
Request::create('/oauth/token', 'post')
);
}
/**
* Validate the users login, checking
* their username/password
*
* @param Request $request
* @return User
*/
public function validateUserLogin($request)
{
$this->incrementLoginAttempts($request);
$username = $request->username;
$password = $request->password;
$user = User::where(['email' => $username])->first();
abort_unless($user, 401, 'Incorrect email/password.');
$user->setVisible(['password']);
abort_unless(Hash::check($password, $user->password), 401, 'Incorrect email/password.');
return $user;
}
}
config/thirdparties.php
<?php
return [
'client_id' => env('THIRDPARTY_CLIENT_ID', null),
];
ThirdParty.php
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class ThirdParty extends Model
{
protected $table = 'oauth_clients';
}
.env
## THIRDPARTIES
THIRDPARTY_CLIENT_ID=3
php artisan make:migration add_scope_to_users_table --table=users
// up
Schema::table('users', function (Blueprint $table) {
$table->text('scopes')->nullable()->after('api_access');
});
// down
Schema::table('users', function (Blueprint $table) {
$table->dropColumn('scopes');
});
(注意:api_access
是一个标志,决定用户是否可以登录到应用程序的 website/frontend 部分,查看 dashboards/records 等),
routes/api.php
Route::group(['middleware' => ['auth.client:YOUR_SCOPE_HERE', 'throttle:60,1']], function () {
...routes...
});
MySQL - Users scopes
INSERT INTO `users` (`id`, `created_at`, `updated_at`, `name`, `email`, `password`, `remember_token`, `api_access`, `scopes`)
VALUES
(5, '2019-03-19 19:27:08', '2019-03-19 19:27:08', '', 'hello@email.tld', 'YOUR_HASHED_PASSWORD', NULL, 1, 'YOUR_SCOPE_HERE ANOTHER_SCOPE_HERE');
MySQL - ThirdParty
Oauth Client
INSERT INTO `oauth_clients` (`id`, `user_id`, `name`, `secret`, `redirect`, `personal_access_client`, `password_client`, `revoked`, `created_at`, `updated_at`)
VALUES
(3, NULL, 'Thirdparty Password Grant Client', 'YOUR_SECRET', 'http://localhost', 0, 1, 0, '2019-03-19 19:12:37', '2019-03-19 19:12:37');
cURL - Logging in/requesting a token
curl -X POST \
http://site.localhost/api/v1/thirdparty/login \
-H 'Accept: application/json' \
-H 'Accept-Charset: application/json' \
-F username=hello@email.tld \
-F password=YOUR_UNHASHED_PASSWORD
{
"token_type": "Bearer",
"expires_in": 604800,
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciO...",
"refresh_token": "def502008a75cd2cdd0dad086..."
}
正常使用 longlived access_token/refresh_token!
Accessing forbidden scope
{
"data": {
"errors": "Invalid scope(s) provided."
},
"meta": {
"code": 403,
"status": "FORBIDDEN"
}
}
我对 laravel 范围部分有点困惑。
我有一个用户模型和 table。
如何为用户分配用户角色,客户 and/or 管理员。
我有一个带有 vue 和 laravel api 后端的 SPA。我用 https://laravel.com/docs/5.3/passport#consuming-your-api-with-javascript
Passport::tokensCan([
'user' => 'User',
'customer' => 'Customer',
'admin' => 'Admin',
]);
我如何分配哪个用户模型具有哪个范围?
或者范围与角色不同?
您将如何实施?
提前致谢!
Or are scopes not the same as roles?
两者最大的区别在于它们适用的上下文。 Role-based 访问控制 (RBAC) 管理用户在 直接 使用 Web 应用程序时的访问控制,而 Oauth-2 范围管理对 API 资源的访问代表用户的 external client。
How can i assign which user model has which scope(s)?
在一般的 Oauth 流程中,要求用户(作为资源所有者)授权客户端代表 his/her 可以做和不能做的事情,这些就是你所说的 范围。 成功授权客户端请求的范围将是assigned to the generated token而不是用户本身。
根据您选择的 Oauth 授权流程,客户端应在其请求中包含范围。在授权代码授权流程中,当将用户重定向到授权页面时,范围应包含在 HTTP GET 查询参数中,而在密码授权流程中,范围必须包含在 HTTP POST body 参数中以请求令牌.
How would you implement this?
这是密码授予流程的示例,假设您事先完成了 laravel/passport 设置
定义管理员和用户角色的范围。尽可能具体,例如:管理员可以 manage-order 而用户只能阅读。
// in AuthServiceProvider boot
Passport::tokensCan([
'manage-order' => 'Manage order scope'
'read-only-order' => 'Read only order scope'
]);
准备 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
}
}
为路由分配 api 守卫和范围
// 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']);
并且在颁发令牌时首先检查用户角色并根据该角色授予范围。为此,我们需要一个额外的控制器,它使用 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);
}
}
为 api 登录端点添加路由
//in api.php
Route::group('namespace' => 'Auth', function () {
Route::post('login', 'ApiLoginController@login');
});
而不是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);
授权成功后,将根据我们之前定义的范围为客户端应用程序发出一个access_token和一个refresh_token。将其保存在某处,并在向 API.
发出请求时将令牌包含到 HTTP header// from client application
$response = $client->request('GET', '/api/my/index', [
'headers' => [
'Accept' => 'application/json',
'Authorization' => 'Bearer '.$accessToken,
],
]);
API现在应该return
{"error":"unauthenticated"}
每当使用权限不足的令牌来使用受限端点时。
实现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'),
]
]
]);
}
如果您将登录:用户名字段更改为自定义用户名字段,例如:e_mail。您必须像在您的 LoginController 中一样改进用户名方法。 此外,您还必须重新定义和编辑方法:validateLogin、attemptLogin、credentials,因为一旦登录被验证,请求就会被转发到 passport 并且必须被称为 username。
我知道这有点晚了,但是如果您在 SPA 中使用 Web 中间件中的 CreateFreshApiToken
使用后端 API,那么您只需添加一个 'admin' 中间件到您的应用程序:
php artisan make:middleware Admin
然后在 \App\Http\Middleware\Admin
中执行以下操作:
public function handle($request, Closure $next)
{
if (Auth::user()->role() !== 'admin') {
return response(json_encode(['error' => 'Unauthorised']), 401)
->header('Content-Type', 'text/json');
}
return $next($request);
}
确保您已将 role
方法添加到 \App\User
以检索用户角色。
现在您需要做的就是在 app\Http\Kernel.php
$routeMiddleware
中注册您的中间件,如下所示:
protected $routeMiddleware = [
// Other Middleware
'admin' => \App\Http\Middleware\Admin::class,
];
并将其添加到您在 routes/api.php
Route::middleware(['auth:api','admin'])->get('/customers','Api\CustomersController@index');
现在,如果您尝试在未经许可的情况下访问 api,您将收到“401 未经授权”错误,您可以在您的应用中检查并处理该错误。
我已经成功地使用@RaymondLagonda 解决方案,使用 Laravel 5.5 和 Sentinel,但是它应该,也可以,没有哨兵。
该解决方案需要覆盖一些 class 方法(因此请记住这一点,以便将来更新),并为您的 api 路由添加一些保护(不公开 client_secret例如)。
第一步,是修改您的 ApiLoginController
以添加构造函数:
public function __construct(Request $request){
$oauth_client_id = env('PASSPORT_CLIENT_ID');
$oauth_client = OauthClients::findOrFail($oauth_client_id);
$request->request->add([
'email' => $request->username,
'client_id' => $oauth_client_id,
'client_secret' => $oauth_client->secret]);
}
在此示例中,您需要在 .env 中定义 var ('PASSPORT_CLIENT_ID') 并创建 OauthClients 模型,但您可以通过在此处放置适当的测试值来安全地跳过此步骤。
需要注意的一件事是,我们将 $request->email
值设置为用户名,只是为了遵守 Oauth2 约定。
第二步是覆盖 sendLoginResponse
方法,该方法会导致 Session storage not set
等错误,我们在这里不需要会话,因为它是 api.
protected function sendLoginResponse(Request $request)
{
// $request->session()->regenerate();
$this->clearLoginAttempts($request);
return $this->authenticated($request, $this->guard()->user())
?: redirect()->intended($this->redirectPath());
}
第三步是,按照@RaymondLagonda 的建议修改经过身份验证的方法。你需要在这里写你自己的逻辑,特别是配置你的作用域。
最后一步(如果您使用的是 Sentinel)是修改 AuthServiceProvider
。添加
$this->app->rebinding('request', function ($app, $request) {
$request->setUserResolver(function () use ($app) {
return \Auth::user();
// return $app['sentinel']->getUser();
});
});
在引导方法中 $this->registerPolicies();
之后。
完成这些步骤后,您应该能够通过提供用户名 ('this will always be email, in this implementation')、密码和 grant_type='password'[=21 让您的 api 正常工作=]
此时,您可以添加中间件范围 scopes:...
或 scope:...
来保护您的路由。
我希望它真的能帮到你...
使用@RaymondLagonda 解决方案。如果您收到 class scopes not found 错误,请将以下中间件添加到 app/Http/Kernel.php
文件的 $routeMiddleware
属性 中:
'scopes' => \Laravel\Passport\Http\Middleware\CheckScopes::class,
'scope' => \Laravel\Passport\Http\Middleware\CheckForAnyScope::class,`
此外,如果您收到错误 Type error: Too few arguments to function
,您应该能够从如下请求中获取 $user
。
(我正在使用 laratrust 管理角色)
public function login(Request $request)
{
$email = $request->input('username');
$user = User::where('email','=',$email)->first();
if($user && $user->hasRole('admin')){
$request->request->add([
'scope' => 'manage-everything'
]);
}else{
return response()->json(['message' => 'Unauthorized'],403);
}
$tokenRequest = Request::create(
'/oauth/token',
'post'
);
return Route::dispatch($tokenRequest);
}
谢谢你,这个问题在我脑海里萦绕了一段时间!我使用 Raymond Lagonda 的解决方案为 Laravel 5.6 定制了一点,使用内置速率限制,使用单个 thirdparty
客户端(或者如果需要的话可以更自定义),同时仍然给每个用户一个列表权限(范围)。
- 使用 Laravel Passport
password
授予并遵循 Oauth 流程 - 使您能够为不同的用户设置角色(范围)
- 不要 expose/release 客户端 ID 或客户端密码,只有用户的用户名(电子邮件)和密码,几乎是 password 授权,减去 client/grant 东西
示例在底部
routes/api.php
Route::group(['namespace' => 'ThirdParty', 'prefix' => 'thirdparty'], function () {
Route::post('login', 'ApiLoginController@login');
});
ThirdParty/ApiLoginController.php
<?php
namespace App\Http\Controllers\ThirdParty;
use Hash;
use App\User;
use App\ThirdParty;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
class ApiLoginController extends Controller
{
use AuthenticatesUsers;
/**
* Thirdparty login method to handle different
* clients logging in for different reasons,
* we assign each third party user scopes
* to assign to their token, so they
* can perform different API tasks
* with the same token.
*
* @param Request $request
* @return Illuminate\Http\Response
*/
protected function login(Request $request)
{
if ($this->hasTooManyLoginAttempts($request)) {
$this->fireLockoutEvent($request);
return $this->sendLockoutResponse($request);
}
$user = $this->validateUserLogin($request);
$client = ThirdParty::where(['id' => config('thirdparties.client_id')])->first();
$request->request->add([
'scope' => $user->scopes,
'grant_type' => 'password',
'client_id' => $client->id,
'client_secret' => $client->secret
]);
return Route::dispatch(
Request::create('/oauth/token', 'post')
);
}
/**
* Validate the users login, checking
* their username/password
*
* @param Request $request
* @return User
*/
public function validateUserLogin($request)
{
$this->incrementLoginAttempts($request);
$username = $request->username;
$password = $request->password;
$user = User::where(['email' => $username])->first();
abort_unless($user, 401, 'Incorrect email/password.');
$user->setVisible(['password']);
abort_unless(Hash::check($password, $user->password), 401, 'Incorrect email/password.');
return $user;
}
}
config/thirdparties.php
<?php
return [
'client_id' => env('THIRDPARTY_CLIENT_ID', null),
];
ThirdParty.php
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class ThirdParty extends Model
{
protected $table = 'oauth_clients';
}
.env
## THIRDPARTIES
THIRDPARTY_CLIENT_ID=3
php artisan make:migration add_scope_to_users_table --table=users
// up
Schema::table('users', function (Blueprint $table) {
$table->text('scopes')->nullable()->after('api_access');
});
// down
Schema::table('users', function (Blueprint $table) {
$table->dropColumn('scopes');
});
(注意:api_access
是一个标志,决定用户是否可以登录到应用程序的 website/frontend 部分,查看 dashboards/records 等),
routes/api.php
Route::group(['middleware' => ['auth.client:YOUR_SCOPE_HERE', 'throttle:60,1']], function () {
...routes...
});
MySQL - Users scopes
INSERT INTO `users` (`id`, `created_at`, `updated_at`, `name`, `email`, `password`, `remember_token`, `api_access`, `scopes`)
VALUES
(5, '2019-03-19 19:27:08', '2019-03-19 19:27:08', '', 'hello@email.tld', 'YOUR_HASHED_PASSWORD', NULL, 1, 'YOUR_SCOPE_HERE ANOTHER_SCOPE_HERE');
MySQL -
ThirdParty
Oauth Client
INSERT INTO `oauth_clients` (`id`, `user_id`, `name`, `secret`, `redirect`, `personal_access_client`, `password_client`, `revoked`, `created_at`, `updated_at`)
VALUES
(3, NULL, 'Thirdparty Password Grant Client', 'YOUR_SECRET', 'http://localhost', 0, 1, 0, '2019-03-19 19:12:37', '2019-03-19 19:12:37');
cURL - Logging in/requesting a token
curl -X POST \
http://site.localhost/api/v1/thirdparty/login \
-H 'Accept: application/json' \
-H 'Accept-Charset: application/json' \
-F username=hello@email.tld \
-F password=YOUR_UNHASHED_PASSWORD
{
"token_type": "Bearer",
"expires_in": 604800,
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciO...",
"refresh_token": "def502008a75cd2cdd0dad086..."
}
正常使用 longlived access_token/refresh_token!
Accessing forbidden scope
{
"data": {
"errors": "Invalid scope(s) provided."
},
"meta": {
"code": 403,
"status": "FORBIDDEN"
}
}