基于company_id属性限制用户页面访问权限的技术实现问询
看起来你正在手动在每个控制器方法里重复编写公司权限检查的逻辑,这不仅冗余,还容易遗漏导致安全问题。下面是几种更优雅的解决方案,帮你消除重复代码,提升代码可维护性:
1. 使用自定义中间件(最适合批量路由控制)
把权限检查逻辑抽成中间件,这样可以在路由组或单个路由上统一应用,不用每个方法都写一遍:
步骤1:创建中间件
php artisan make:middleware CheckCompanyOwnership
步骤2:编写中间件逻辑
打开app/Http/Middleware/CheckCompanyOwnership.php,替换内容:
<?php namespace App\Http\Middleware; use Closure; use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\DB; class CheckCompanyOwnership { public function handle(Request $request, Closure $next) { // 确保用户已登录(如果路由没加auth中间件,需要这一步) if (!Auth::check()) { abort(401, '请先登录'); } $userCompanyId = Auth::user()->company_id; // 从URL获取资源ID和对应的公司ID(根据你的路由参数名调整) $resourceId = $request->route('p'); $resourceCompanyId = $request->route('c'); // 验证当前用户的公司是否拥有该资源 $isAuthorized = DB::table('sellers') ->where('id', $resourceId) ->where('company_id', $userCompanyId) ->exists(); if (!$isAuthorized) { abort(403, '你无权访问该资源'); } return $next($request); } }
步骤3:注册中间件
在app/Http/Kernel.php的$routeMiddleware数组里添加:
protected $routeMiddleware = [ // ... 其他中间件 'check.company' => \App\Http\Middleware\CheckCompanyOwnership::class, ];
步骤4:应用到路由或控制器
- 路由级别:给需要检查的路由加上中间件
Route::prefix('customer_question') ->middleware(['auth', 'check.company']) ->group(function () { Route::get('/p/{p}/c/{c}', [CustomerQuestionController::class, 'show']); Route::post('/p/{p}/c/{c}', [CustomerQuestionController::class, 'update']); // 其他需要检查的路由 });
- 控制器级别:在控制器构造函数里指定哪些方法需要检查
public function __construct() { $this->middleware(['auth', 'check.company'])->only(['show', 'update', 'destroy']); }
2. 使用Laravel Policy(最适合基于模型的细粒度权限)
如果你用Eloquent模型(比如Seller模型),Policy是更符合Laravel设计理念的方案,能实现更细粒度的权限控制:
步骤1:创建Policy
php artisan make:policy SellerPolicy --model=Seller
步骤2:编写Policy逻辑
打开app/Policies/SellerPolicy.php,添加权限检查方法:
<?php namespace App\Policies; use App\Models\Seller; use App\Models\User; use Illuminate\Auth\Access\Response; class SellerPolicy { // 查看单个资源的权限 public function view(User $user, Seller $seller): Response { return $user->company_id === $seller->company_id ? Response::allow() : Response::deny('你无权访问该资源'); } // 更新资源的权限 public function update(User $user, Seller $seller): Response { return $this->view($user, $seller); // 复用view的检查逻辑 } // 同理可以定义delete、create等方法 }
步骤3:在控制器中使用Policy
在控制器方法里调用authorize方法,自动触发权限检查:
use App\Models\Seller; public function show(Seller $seller) { // 自动检查当前用户是否有view该seller的权限 $this->authorize('view', $seller); // 后续业务逻辑 return view('customer_question.show', compact('seller')); } public function update(Request $request, Seller $seller) { $this->authorize('update', $seller); // 更新逻辑 $seller->update($request->validated()); return redirect()->route('customer_question.show', $seller); }
进阶:自动授权资源
如果控制器遵循RESTful风格,可以在构造函数里用authorizeResource自动为所有CRUD方法绑定权限检查:
public function __construct() { $this->authorizeResource(Seller::class, 'seller'); }
这样index、show、create、store、edit、update、destroy方法会自动对应Policy里的viewAny、view、create、store、update、destroy方法。
3. 控制器基类(适合多个控制器共享相同检查逻辑)
如果多个控制器都需要类似的公司权限检查,可以创建一个基类,把检查逻辑写在基类里,其他控制器继承它:
<?php namespace App\Http\Controllers; use Illuminate\Foundation\Auth\Access\AuthorizesRequests; use Illuminate\Foundation\Bus\DispatchesJobs; use Illuminate\Foundation\Validation\ValidatesRequests; use Illuminate\Routing\Controller as BaseController; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\DB; class BaseCompanyController extends BaseController { use AuthorizesRequests, DispatchesJobs, ValidatesRequests; protected function checkCompanyOwnership($resourceId) { $userCompanyId = Auth::user()->company_id; $isAuthorized = DB::table('sellers') ->where('id', $resourceId) ->where('company_id', $userCompanyId) ->exists(); if (!$isAuthorized) { abort(403, '你无权访问该资源'); } } }
然后你的控制器继承这个基类,在需要的方法里调用:
class CustomerQuestionController extends BaseCompanyController { public function show($p) { $this->checkCompanyOwnership($p); // 业务逻辑 } }
推荐方案
- 如果是批量路由的通用检查,优先用中间件
- 如果是基于Eloquent模型的CRUD操作,优先用Policy(更符合Laravel的权限体系)
- 如果只是少数几个控制器共享逻辑,可以用控制器基类
另外,建议尽量使用Eloquent模型代替DB::table,这样代码更简洁,也能利用模型的关联关系,比如如果User和Seller有hasMany关联,检查逻辑可以写成:
$isAuthorized = Auth::user()->sellers()->where('id', $resourceId)->exists();
内容的提问来源于stack exchange,提问作者SNaRe

