我目前正在构建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中执行此操作,问题是密码被哈希,这意味着无法询问在登录期间没有密码的密码授予类型的令牌?
答案 0 :(得分:-1)
护照默认提供,但也无法正常工作
Passport::actingAs($user,['scope']);
public static function actingAs($user, $scopes = [], $guard = 'api')