如何使用Laravel Sanctum保护无需登录的API接口
客户端令牌保护公开路由的合理性判断
这种做法完全合理,属于API服务安全的常规操作,核心价值有三个:
- 拦截未授权的第三方恶意调用、爬虫批量爬取公开接口,降低服务被刷、带宽被占的风险
- 可以针对不同客户端做差异化的流量限制、功能权限控制
- 避免公网无差别攻击直接打到你的业务接口上,相当于给公开接口加了一层基础防护
Laravel Sanctum 实现相同效果的方案
Sanctum 本身没有内置 Passport 那种客户端凭据授权的功能,你可以通过自定义中间件的方式快速实现,步骤如下:
步骤1:新增客户端标识存储
如果没有客户端表就新建clients表,至少包含以下字段:
- 客户端ID
client_id:字符串类型,唯一标识每个合法客户端(比如安卓端、IOS端、H5端各分配一个) - 客户端密钥
client_secret:字符串类型,和client_id配对校验 - 客户端名称
name:备注用,方便识别客户端类型 - 状态
status:控制客户端是否有效,禁用后对应客户端无法调用接口
如果你的合法客户端数量很少(比如只有3个以内的官方客户端),可以不用做动态令牌签发,直接给每个客户端生成固定的令牌存在
clients表,中间件直接校验请求带的令牌是否在有效客户端的令牌列表里即可,实现更简单,适合小项目使用。
步骤2:实现客户端令牌签发接口
写一个公开的令牌签发接口,请求方传入合法的client_id和client_secret后返回有效期可控的令牌,令牌可以用Laravel自带的加密工具生成,存储到缓存或者数据库里关联对应的客户端ID,核心示例代码:
// 令牌签发逻辑 public function issueToken(Request $request) { $validated = $request->validate([ 'client_id' => 'required|string', 'client_secret' => 'required|string', ]); $client = Client::where('client_id', $validated['client_id']) ->where('client_secret', $validated['client_secret']) ->where('status', 1) ->first(); if (!$client) { return response()->json(['message' => '无效的客户端凭证'], 401); } // 生成有效期7天的令牌,可按需调整时长 $token = Str::random(64); Cache::put('client_token:'.$token, $client->id, now()->addDays(7)); return response()->json(['access_token' => $token, 'expires_in' => 604800]); }
步骤3:自定义客户端鉴权中间件
执行命令生成中间件php artisan make:middleware ClientAuth,实现令牌校验逻辑:
public function handle(Request $request, Closure $next) { $token = $request->bearerToken(); if (!$token || !Cache::has('client_token:'.$token)) { return response()->json(['message' => '无效的客户端令牌'], 401); } // 可将客户端ID存入请求实例,后续业务需要的话可以直接调用 $request->merge(['client_id' => Cache::get('client_token:'.$token)]); return $next($request); }
之后在app/Http/Kernel.php的$routeMiddleware数组里注册中间件:
'auth.client' => \App\Http\Middleware\ClientAuth::class,
步骤4:路由分组配置
在路由文件里拆分两类路由的中间件即可:
// 需要用户登录的路由,用sanctum原生中间件 Route::middleware('auth:sanctum')->group(function () { // 个人资料、订单、收件箱等需要用户身份的接口写在这里 }); // 不需要用户登录但需要校验客户端合法性的路由,用自定义的auth.client中间件 Route::middleware('auth.client')->group(function () { // API版本、启动页、忘记密码等公开接口写在这里 }); // 完全公开不需要任何校验的接口(如果有的话)不需要加任何鉴权中间件
内容的提问来源于stack exchange,提问作者Toto Prasetyo
相关产品推荐
相关产品推荐

