如何在前端应用中正确使用Laravel Sanctum令牌?
解决方案:Laravel Sanctum 单应用 API 复用与权限验证优化
你的核心问题在于硬编码令牌无法适配多用户场景,且通过 HTTP 调用自身 API 存在冗余开销。以下是针对你的场景的几种可行方案,按推荐优先级排序:
方案一:直接复用业务逻辑(最优)
既然是同一个应用,完全没必要通过 HTTP 请求调用自身 API,抽离共享逻辑到服务层即可实现复用,同时避免令牌传递的麻烦。
步骤1:封装共享业务逻辑到服务类
创建服务类统一处理产品查询与权限验证:
namespace App\Services; use App\Models\User; use App\Models\Product; use Illuminate\Http\Exceptions\HttpResponseException; class ProductService { public function getProducts(User $user) { // 验证用户令牌权限(管理员分配令牌时需指定对应abilities) if (!$user->tokenCan('view:products')) { throw new HttpResponseException(response()->json([ 'message' => '无产品查看权限' ], 403)); } return Product::query()->get(); } }
步骤2:API 控制器调用服务类
namespace App\Http\Controllers\Api\V1; use App\Services\ProductService; use Illuminate\Http\Request; class ProductsController extends Controller { public function index(Request $request, ProductService $service) { $products = $service->getProducts($request->user()); return response()->json($products); } }
步骤3:前端控制器直接调用服务类
namespace App\Http\Controllers; use App\Services\ProductService; use Illuminate\Support\Facades\Auth; class ProductViewController extends Controller { // 确保路由已添加 auth 中间件,限制仅登录用户访问 public function index(ProductService $service) { $user = Auth::user(); $products = $service->getProducts($user); return view('products', ['products' => $products]); } }
方案二:通过 Sanctum 会话认证调用自身 API
如果坚持要通过 HTTP 调用 API,可利用 Sanctum 的会话认证能力,无需手动传递令牌:
1. 配置 Sanctum 信任域名
在 config/sanctum.php 中添加自身域名到 stateful 数组:
'stateful' => explode(',', env('SANCTUM_STATEFUL_DOMAINS', sprintf( '%s%s', 'localhost,localhost:3000,127.0.0.1,127.0.0.1:8000,::1', env('APP_URL') ? ','.parse_url(env('APP_URL'), PHP_URL_HOST) : '' ))),
2. 修改前端控制器代码
当前用户通过 Web 登录后,Sanctum 会自动维护会话认证状态,调用 API 时无需手动携带令牌:
public function index() { // 路由需添加 auth 中间件,确保用户已登录 $products = Http::get(env('APP_URL').'/api/v1/products'); return view('products', ['products' => $products->json()]); }
3. API 路由保持原有配置
确保 API 路由使用 auth:sanctum 中间件,同时兼容外部令牌调用与内部会话认证:
Route::middleware('auth:sanctum')->group(function () { Route::get('/api/v1/products', [\App\Http\Controllers\Api\V1\ProductsController::class, 'index']); });
方案三:手动传递用户令牌(仅特殊场景使用)
如果必须手动传递令牌,需注意:数据库中存储的令牌是哈希值,无法直接读取,必须在创建令牌时保存原始令牌值(建议加密存储)。
1. 创建令牌时保存原始值
管理员分配令牌时,将生成的原始令牌加密后存入用户表(需先给用户表添加 api_token 字段):
// 管理员创建用户令牌示例 $token = $user->createToken('产品访问令牌', ['view:products'])->plainTextToken; $user->update(['api_token' => encrypt($token)]);
2. 前端控制器获取并使用令牌
public function index() { $user = Auth::user(); // 解密获取原始令牌 $token = decrypt($user->api_token); $products = Http::withToken($token) ->get(env('APP_URL').'/api/v1/products'); return view('products', ['products' => $products->json()]); }
注意:此方案存在安全风险,令牌值存储在数据库中(即使加密),不如会话认证安全,仅在特殊场景下使用。
内容的提问来源于stack exchange,提问作者BobbyP
相关产品推荐
相关产品推荐

