Laravel 5.1 ACL 路由资源不工作
Laravel 5.1 ACL route resource not working
在遵循 tutorial 关于 laravel 的内置 acl 如何工作之后,我尝试了它,它通过自己定义每条路由来很好地工作。
现在我正在尝试使用资源,但它没有按预期工作。我将以下代码添加到我的路由文件中:
Route::group(['middleware' => 'acl:create_client'], function()
{
Route::resource('clients', 'ClientController');
});
现在我明白问题是什么了:
如果此用户具有 acl:create_client,将根据我的数据库检查 Clientcontroller 中的所有方法,从而使具有此 acl 的登录用户可以使用所有方法。
如何拆分每个方法以使用它自己的 acl 而不必像这样编写:
Route::get('/client/create', [
'middleware' => 'acl:create_client',
'as' => 'clients.create',
'uses' => 'ClientController@create'
]);
结果是这样的:
创造需求create_client
索引需要index_client
更新需要update_client
等等等等
底线是:您需要以某种方式在访问控制列表 (ACL) 中设置 'list'。 IMO,最灵活的方法是根据会话用户从数据库中提取此列表;你有一个好的开始。您可以使用您在路由中定义的已分配 'as'
来跳过显式路由分配。示例路线:
Route::get('/', ['as'=>'clients.create', 'uses'=>'ClientsController@create']);
在这里,您将在 ACL 检查中使用 'clients.create'
。请记住:ACL 仍然需要为所有路由设置 'as'
值(无论如何这样做都很好)。
Step-by-step
既然您已经掌握了所需的背景信息,下面就是如何让它发挥作用的方法。这些步骤假定您能够正确设置教程代码和数据库。这将坚持原来的教程设置,并将重点放在使 ACL 独立于路由配置。
1) 在 App\Http\Middleware\Acl\CheckPermission
中,您需要将参数 $permission = null
替换为您在 routes.php
中设置的 'as'
字符串。新代码:
<?php namespace App\Http\Middleware;
use Closure;
class CheckPermission
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next/*, $permission = null REMOVE THIS*/)
{
// Add the next two lines:
$action = $request->route()->getAction();
$permission = isset($action['as']) ? $action['as'] : '';
if (!app('Illuminate\Contracts\Auth\Guard')->guest()) {
if ($request->user()->can($permission)) {
return $next($request);
}
}
return $request->ajax ? response('Unauthorized.', 401) : redirect('/login');
}
}
2) 现在,您需要以不同的方式分配此中间件。您不想使用特定权限,而是使用我们刚刚在中间件中设置的 'as'
字符串。您可以通过两种不同的方式分配中间件:a) 将其分配给一组路由,或 b) 将其分配给每个页面。我建议使用 2a 而不是 2b,因为您可能不想在所有路由上都使用 ACL。
2a) 下面是将它分配给一组路由的方法。这里需要注意的两个重要事项是 'as'=>'clients.*'
字符串和将中间件分配给路由组 'middleware' => 'acl'
。另请注意,此路由组不会像教程那样传递额外的字符串参数(例如 'middleware' => 'acl:manage_user'
)。这是因为我们从上面的 handle()
函数中删除了该参数。您将需要更改这些示例路由以匹配您的目标 URI 和控制器功能。
Route::group(['middleware' => 'acl'], function()
{
Route::get('/clients', ['as'=>'clients.view', 'uses'=>'ClientsController@index']);
Route::get('/clients/new', ['as'=>'clients.create', 'uses'=>'ClientsController@create']);
// Add more routes ...
}
2b) 以下是将其分配给每个页面的方法。本教程使用文件 /app/Http/Kernel.php
将中间件设置为 $routeMiddleware
。这是执行上述步骤 2a 的正确方法,但如果您希望在每个页面上都这样做,则不是。要使中间件成为全局中间件:将 '\App\Http\Middleware\CheckPermission'
添加到同一文件中的 $middleware
变量。如果您使用全局变量,则不需要本教程中的 $routeMiddleware
添加项。
3) 在教程数据库中,需要在permission_slug
列中的permissions
table中使用'as'
字符串。下面是示例 SQL 插入,允许 ID 为 123
的用户访问路由 clients.create
。这两个创建了我们创建对 'client.create'
路由的访问所需的权限和角色。
INSERT INTO permissions ('permission_title', 'permission_slug', 'permission_description')
VALUES ('Create a Client', 'clients.create', 'Allow the user to create a client');
INSERT INTO roles ('role_title', 'role_slug')
VALUES ('Client Admin', 'clients.admin');
接下来的查询,需要知道上面两行的id
。这假设您的数据库是新创建的,尚未添加任何行,因此每个插入都是 id=1
。这表示:id=1
的权限分配给 id=1
的角色。
INSERT INTO permission_role ('permission_id', 'role_id') VALUES (1, 1);
下一个查询还假定新角色为 id=1
,用户 ID 为 123
。这会将 id=1
的新角色分配给 id=123
.
的现有用户
INSERT INTO role_user ('role_id', 'user_id') VALUES (1, 123);
此时,您应该有一个具有 id=123
且具有 Client Admin
角色的用户。 Client Admin
角色应具有 'clients.create'
权限。当您以用户 id=123
身份登录时,您将被验证为具有 'clients.create'
权限,并且您应该能够访问该页面(在我的示例中为 example.com/clients/new
)。任何其他用户将无法访问,他们将被重定向到登录页面(如果您已经登录,这对我来说没有意义;这正是教程设置)。
我建议您不要自己构建 acl,那里有一些不错的软件包,例如 entrust
如果您真的想了解原理或 laravel acl,请按照 laracast laracast laravel acl tutorial
的视频教程进行操作
在遵循 tutorial 关于 laravel 的内置 acl 如何工作之后,我尝试了它,它通过自己定义每条路由来很好地工作。
现在我正在尝试使用资源,但它没有按预期工作。我将以下代码添加到我的路由文件中:
Route::group(['middleware' => 'acl:create_client'], function()
{
Route::resource('clients', 'ClientController');
});
现在我明白问题是什么了:
如果此用户具有 acl:create_client,将根据我的数据库检查 Clientcontroller 中的所有方法,从而使具有此 acl 的登录用户可以使用所有方法。
如何拆分每个方法以使用它自己的 acl 而不必像这样编写:
Route::get('/client/create', [
'middleware' => 'acl:create_client',
'as' => 'clients.create',
'uses' => 'ClientController@create'
]);
结果是这样的:
创造需求create_client
索引需要index_client
更新需要update_client
等等等等
底线是:您需要以某种方式在访问控制列表 (ACL) 中设置 'list'。 IMO,最灵活的方法是根据会话用户从数据库中提取此列表;你有一个好的开始。您可以使用您在路由中定义的已分配 'as'
来跳过显式路由分配。示例路线:
Route::get('/', ['as'=>'clients.create', 'uses'=>'ClientsController@create']);
在这里,您将在 ACL 检查中使用 'clients.create'
。请记住:ACL 仍然需要为所有路由设置 'as'
值(无论如何这样做都很好)。
Step-by-step
既然您已经掌握了所需的背景信息,下面就是如何让它发挥作用的方法。这些步骤假定您能够正确设置教程代码和数据库。这将坚持原来的教程设置,并将重点放在使 ACL 独立于路由配置。
1) 在 App\Http\Middleware\Acl\CheckPermission
中,您需要将参数 $permission = null
替换为您在 routes.php
中设置的 'as'
字符串。新代码:
<?php namespace App\Http\Middleware;
use Closure;
class CheckPermission
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next/*, $permission = null REMOVE THIS*/)
{
// Add the next two lines:
$action = $request->route()->getAction();
$permission = isset($action['as']) ? $action['as'] : '';
if (!app('Illuminate\Contracts\Auth\Guard')->guest()) {
if ($request->user()->can($permission)) {
return $next($request);
}
}
return $request->ajax ? response('Unauthorized.', 401) : redirect('/login');
}
}
2) 现在,您需要以不同的方式分配此中间件。您不想使用特定权限,而是使用我们刚刚在中间件中设置的 'as'
字符串。您可以通过两种不同的方式分配中间件:a) 将其分配给一组路由,或 b) 将其分配给每个页面。我建议使用 2a 而不是 2b,因为您可能不想在所有路由上都使用 ACL。
2a) 下面是将它分配给一组路由的方法。这里需要注意的两个重要事项是 'as'=>'clients.*'
字符串和将中间件分配给路由组 'middleware' => 'acl'
。另请注意,此路由组不会像教程那样传递额外的字符串参数(例如 'middleware' => 'acl:manage_user'
)。这是因为我们从上面的 handle()
函数中删除了该参数。您将需要更改这些示例路由以匹配您的目标 URI 和控制器功能。
Route::group(['middleware' => 'acl'], function()
{
Route::get('/clients', ['as'=>'clients.view', 'uses'=>'ClientsController@index']);
Route::get('/clients/new', ['as'=>'clients.create', 'uses'=>'ClientsController@create']);
// Add more routes ...
}
2b) 以下是将其分配给每个页面的方法。本教程使用文件 /app/Http/Kernel.php
将中间件设置为 $routeMiddleware
。这是执行上述步骤 2a 的正确方法,但如果您希望在每个页面上都这样做,则不是。要使中间件成为全局中间件:将 '\App\Http\Middleware\CheckPermission'
添加到同一文件中的 $middleware
变量。如果您使用全局变量,则不需要本教程中的 $routeMiddleware
添加项。
3) 在教程数据库中,需要在permission_slug
列中的permissions
table中使用'as'
字符串。下面是示例 SQL 插入,允许 ID 为 123
的用户访问路由 clients.create
。这两个创建了我们创建对 'client.create'
路由的访问所需的权限和角色。
INSERT INTO permissions ('permission_title', 'permission_slug', 'permission_description')
VALUES ('Create a Client', 'clients.create', 'Allow the user to create a client');
INSERT INTO roles ('role_title', 'role_slug')
VALUES ('Client Admin', 'clients.admin');
接下来的查询,需要知道上面两行的id
。这假设您的数据库是新创建的,尚未添加任何行,因此每个插入都是 id=1
。这表示:id=1
的权限分配给 id=1
的角色。
INSERT INTO permission_role ('permission_id', 'role_id') VALUES (1, 1);
下一个查询还假定新角色为 id=1
,用户 ID 为 123
。这会将 id=1
的新角色分配给 id=123
.
INSERT INTO role_user ('role_id', 'user_id') VALUES (1, 123);
此时,您应该有一个具有 id=123
且具有 Client Admin
角色的用户。 Client Admin
角色应具有 'clients.create'
权限。当您以用户 id=123
身份登录时,您将被验证为具有 'clients.create'
权限,并且您应该能够访问该页面(在我的示例中为 example.com/clients/new
)。任何其他用户将无法访问,他们将被重定向到登录页面(如果您已经登录,这对我来说没有意义;这正是教程设置)。
我建议您不要自己构建 acl,那里有一些不错的软件包,例如 entrust
如果您真的想了解原理或 laravel acl,请按照 laracast laracast laravel acl tutorial
的视频教程进行操作