Laravel:根据访问控制列表设置动态路由

时间:2018-02-13 02:27:55

标签: php laravel laravel-5.5

我正在使用自己的逻辑使用JWT身份验证和授权构建 REST API 。它工作得很好。现在,我想根据角色和权限动态设置路由。假设我有数据库结构,如:

作用:

id  |   name
1   |  school
2   | transport

权限:

id  |   name                   |  controller         | routes
1   |  view-class-result       |  ApiController      | getClassResult
2   |  view-student-result     |  ApiController      | studentResult
3   |  download-student-result |  ApiController      | donwloadSchoolTemplate

Permission_role

role_id |  permission_id
1            1
1            2
1            3

现在,我想根据数据库中的角色和权限创建路由。

目前我的路线似乎是:

//All JWT authentication API goes here
Route::group(['middleware' => 'jwt.auth'], function() {
   Route::get('user', 'ApiController@getAuthUser');
   Route::get('invalidate', 'ApiController@invalidate');

   //All authorized API goes here
   Route::group(['middleware' => 'ability:school,view-class-result,true'], function() {
       Route::post('classResult', 'ApiController@getClassResult');
   });
   Route::group(['middleware' => 'ability:school,view-student-result,true'], function() {
       Route::post('studentResult', 'ApiController@studentResult');
   });
   Route::group(['middleware' => 'ability:school,download-student-result,true'], function() {
       Route::post('getStudentExamResult', 'ApiController@downloadSchoolTemplate');
   });
});

我不希望上面的路线被硬编码。如何从数据库中获取此路由。像下面的东西。但是不知道该怎么做。

在路线档案中,

$a = User:all();
foreach($a->roles as $value){
   foreach($value->permission as $val){

      Route::group(['middleware' => 'ability:{$value->name},{$val->name},true'], function() {
         Route::post('{$val->controller}', '{$val->controller}@{$val->method}');
      });

   }
}

谢谢。

4 个答案:

答案 0 :(得分:9)

最好的想法是使用中间件参数 创建中间件调用 CheckPermission 然后您必须将该中间件注册到 app / Http / kernel.php 文件中,只需要检查下面的代码< / p>

您的 kernel.php 文件

protected $routeMiddleware = [    
        'checkPermission' => \App\Http\Middleware\CheckPermission::class,
    ];

<强> CheckPermission.php

    <?php

    namespace App\Http\Middleware;
    use Closure;
    use DB;

    class CheckPermission
    {
        /**
         * Handle an incoming request.
         *
         * @param  \Illuminate\Http\Request  $request
         * @param  \Closure  $next
         * @return mixed
         */
        public function handle($request, Closure $next,$permission_name)
        {
            //first check that name in your db
            $permission = DB::table('Permission')->where('name',$permission_name)->first()
            if($permission){
              //here you have to get logged in user role
              $role_id = Auth::user()->role;
              ## so now check permission
              $check_permission = DB::table('Permission_role')->where('role_id',$role_id)->where('permission_id',$permission->id)->first();
              if($check_permission){
                 return $next($request);
              }
              //if Permission not assigned for this user role show what you need
            }
            // if Permission name not in table then do what you need 
            ## Ex1 : return 'Permission not in Database';
            ## Ex2 : return redirect()->back();

        }
    }

您的路线文件

 Route::group(['middleware' => 'jwt.auth'], function() {
        Route::post('classResult', 'ApiController@getClassResult')->middleware('checkPermission:view-class-result');
        Route::post('studentResult', 'ApiController@studentResult')->middleware('checkPermission:view-student-result');
        Route::post('getStudentExamResult', 'ApiController@downloadSchoolTemplate')->middleware('checkPermission:download-student-result');

   }

答案 1 :(得分:0)

So what you can do is make your role name accountants a value to key in the .env file and same for each and every role name.

In case you want to change it in near future you can change it manually in the .env file or you can make changes in the .env file via php code writtern on one of your Laravel function.

答案 2 :(得分:0)

虽然我怀疑这是最好的方法,但是以你的思维方式你可以试试这个&#34;伪代码&#34;。我希望这表达了基本的想法。这意味着:

  • 路由模式,不要在路由文件中明确包含所有路由。即。 api/studentResult
  • 您的控制器通过单个操作控制器(Link to documentation)发送到实现您的api呼叫的正确方法
  • 您的控制器加载正确的中间件以注意授权

路线

Route::group(['middleware' => 'jwt.auth'], function() {
    Route::get('user', 'ApiController@getAuthUser');
    Route::get('invalidate', 'ApiController@invalidate');

    // Choose whatever pattern you like...
    Route::post('api/{name}', ApiController::class);
});

控制器

class ApiController {

    public function __construct() {
        $permisions = $this->loadPersionForUser();

        $this->middleware('ability', [$permisions->value1, 'whatever']);
    }

    public function __invoke($method) {
        if (method_exists($this, $method)) {
            return $this->$method();
        }
    }
}

我不完全确定您是否可以像这样动态加载中间件。如果是这样,这可能是一种有效的方法。

答案 3 :(得分:0)

routes.php

Route::group(['middleware' => 'jwt.auth'], function() {

Route::post('{uri}', 'AccessController@redirectURI');

});

在所有路线的末尾添加此路线。

现在创建一个名为AccessController的新控制器,并在其中添加constructormethod。 假设用户与角色有关系。

public function __construct(Request $request)
{
    $authorised_user = User::where('id', Auth::User()->id)->whereHas('role', function($query) use ($request)
            {
                $query->whereHas('permission', function($query) use ($request)
                {
                    $query->where('routes', $request->route('uri'))
                });
            })->firstOrFail();

            if( $authorised_user )
            {
                $permission = Permission::where('routes', $request->route('uri'))->findOrFail();

                $this->middleware([ 'ability:'.$authorised_user->role()->name.','.$permission->name.',true' ]);
            }
    else
    {
    // user is not authorised. Do what ever you want
    }

}

public function redirectURI($uri)
{
    $permission = Permission::where('routes', $uri)->findOrFail();

    return app('App\\Http\\Controllers\\'. $permission->controller )->$permission->method();
}

总体而言,它正在检索URL并将其与经过身份验证的用户权限中的可用路由进行比较。如果经过身份验证的用户具有该路由所属的权限,则添加适当的中间件。

最后,redirectURI方法调用适当的控制器方法并返回响应。

请记住在必要时用适当的命名空间和关系替换代码。