Laravel: настройка динамических маршрутов на основе списка управления доступом
Я создаю REST API с JWT
аутентификацией и авторизацией с собственной логикой. Он отлично работает. Теперь я хочу настроить маршруты динамически на основе ролей и разрешений. Предположим, у меня есть структура базы данных, например:
Роль:
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', '[email protected]');
Route::get('invalidate', '[email protected]');
//All authorized API goes here
Route::group(['middleware' => 'ability:school,view-class-result,true'], function() {
Route::post('classResult', '[email protected]');
});
Route::group(['middleware' => 'ability:school,view-student-result,true'], function() {
Route::post('studentResult', '[email protected]');
});
Route::group(['middleware' => 'ability:school,download-student-result,true'], function() {
Route::post('getStudentExamResult', '[email protected]');
});
});
Я не хочу, чтобы выше маршруты были жестко закодированы. Как я могу получить эти маршруты из базы данных. Что-то вроде ниже. Но не мог понять, как это сделать.
В файле маршрутов
$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}');
});
}
}
Спасибо.
Ответы
Ответ 1
Лучшая идея заключалась в использовании параметра промежуточного программного обеспечения
создайте посланное вызов CheckPermission, тогда вам нужно зарегистрировать это промежуточное программное обеспечение в файле app/Http/kernel.php, который вам нужен только под кодом
Ваш файл 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', '[email protected]')->middleware('checkPermission:view-class-result');
Route::post('studentResult', '[email protected]')->middleware('checkPermission:view-student-result');
Route::post('getStudentExamResult', '[email protected]')->middleware('checkPermission:download-student-result');
}
Ответ 2
Так что вы можете сделать свое имя роли бухгалтеров в .env файл и тот же для каждого имени роли.
Если вы хотите изменить его в ближайшем будущем, вы можете изменить его вручную в файле .env или вы можете внести изменения в файл .env через php на одной из ваших функций Laravel.
Ответ 3
Хотя я сомневаюсь, что это лучший подход к этому, но по вашему образу мышления вы можете попробовать этот "псевдокод". Надеюсь, это выражает основную идею. Это подразумевает следующее:
- Шаблон маршрута, чтобы не включать все маршруты в файл маршрутов. то есть.
api/studentResult
- Ваш контроллер должен отправить соответствующий метод, который реализует ваш вызов api через один контроллер действий (Ссылка на документацию)
- Ваш контроллер загрузит правильное промежуточное ПО, чтобы заботиться о его авторизации
Маршруты
Route::group(['middleware' => 'jwt.auth'], function() {
Route::get('user', '[email protected]');
Route::get('invalidate', '[email protected]');
// 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();
}
}
}
Я не совсем уверен, что вы можете динамически загружать свое промежуточное программное обеспечение. Если это так, это может быть приемлемым для этого.
Ответ 4
В routes.php
Route::group(['middleware' => 'jwt.auth'], function() {
Route::post('{uri}', '[email protected]');
});
Добавьте этот маршрут в конце всех ваших маршрутов.
Теперь создайте новый контроллер под названием AccessController
и добавьте к нему ниже constructor
и method
.
Предполагая, что пользователь имеет отношения с ролями.
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
вызывает соответствующий метод контроллера и возвращает ответ.
Не забудьте заменить код соответствующим пространством имен и отношениями, когда это необходимо.