0

0

在 Laravel 中验证第三方 JWT 的专业指南

心靈之曲

心靈之曲

发布时间:2025-12-02 12:55:00

|

478人浏览过

|

来源于php中文网

原创

在 Laravel 中验证第三方 JWT 的专业指南

本文详细介绍了如何在 laravel 应用中验证来自外部身份提供商的 json web token (jwt)。我们将利用 `tymondesigns/jwt-auth` 库,并通过自定义 guard 实现 jwt 的解析、rs256 签名验证(包括 jwks 公钥配置)、标准声明检查以及应用权限(scope)验证。教程将涵盖代码实现、配置步骤及关键注意事项,帮助开发者构建安全可靠的微服务认证体系。

在 Laravel 中验证第三方 JWT

在现代微服务架构中,利用外部身份提供商(IdP)进行用户认证已成为常见实践。当用户通过 IdP 认证后,客户端会获得一个访问令牌(Access Token),通常以 JWT 形式存在。为了确保 API 服务的安全性,后端微服务需要对收到的 JWT 进行严格验证。本教程将指导您如何在 Laravel 框架中实现这一过程,特别关注 RS256 签名的验证和自定义 Guard 的应用。

JWT 验证的核心步骤

验证一个第三方 JWT 通常涉及以下几个关键步骤:

  1. 解析 JWT:将 JWT 从 Bearer 头中提取出来,并解析其头部(Header)、载荷(Payload)和签名(Signature)部分。
  2. 验证签名:这是最关键的一步,确保令牌未被篡改。对于使用 RS256 算法签名的 JWT,需要使用 IdP 提供的公钥进行验证。这些公钥通常通过 JWKS (JSON Web Key Set) URL 获取。
  3. 验证标准声明:检查 JWT 的过期时间 (exp)、生效时间 (nbf)、签发时间 (iat)、签发者 (iss) 和受众 (aud) 等标准声明,确保令牌在有效期内且针对正确的服务。
  4. 验证应用权限(Scopes):检查 JWT 载荷中包含的权限范围(scope 或 scp 声明),以确定用户是否有权访问请求的资源。

虽然 Laravel 生态中有许多用于创建 JWT 的包,但直接用于验证第三方 JWT 的集成方案可能不那么直观。我们将使用 tymondesigns/jwt-auth 包作为基础,并结合自定义 Guard 来实现上述验证逻辑。

1. 配置 JWT 公钥

对于使用 RS256 算法签名的 JWT,您需要从 IdP 的 JWKS URL 获取公钥。JWKS URL 通常是 https://{domain}/.well-known/jwks.json。从该 URL 获取到正确的公钥后,将其保存为 .pem 格式的文件,例如 storage/jwt/public.pem。

接下来,修改 config/jwt.php 配置文件,指示 tymondesigns/jwt-auth 使用该公钥进行签名验证:

// config/jwt.php

return [
    // ...
    'keys' => [
        'public' => 'file://' . storage_path('jwt/public.pem'),
        'private' => null, // 验证第三方JWT不需要私钥
        'passphrase' => null,
    ],
    'algo' => 'RS256', // 指定签名算法为RS256
    // ...
];

注意:在生产环境中,直接下载公钥文件并硬编码路径可能不是最佳实践。更健壮的方案是动态地从 JWKS URL 获取公钥,并根据 JWT 头部中的 kid (Key ID) 选择正确的公钥,然后进行缓存。但在本教程中,为简化示例,我们采用本地文件的方式。

2. 实现自定义 JWT Guard

Laravel 的认证系统允许您通过自定义 Guard 来扩展其功能。我们将创建一个 JWTGuard 类,负责从请求中提取 JWT、调用 tymondesigns/jwt-auth 进行验证,并根据 JWT 载荷创建用户实例。

app/Guard 目录下创建 JWTGuard.php 文件:

<?php

namespace App\Guard;

use App\Models\User; // 假设您有一个自定义的User模型
use Illuminate\Auth\GuardHelpers;
use Illuminate\Contracts\Auth\Guard;
use Illuminate\Http\Request;
use Tymon\JWTAuth\JWT;

class JWTGuard implements Guard
{
    use GuardHelpers;

    /**
     * @var JWT $jwt
     */
    protected JWT $jwt;

    /**
     * @var Request $request
     */
    protected Request $request;

    /**
     * JWTGuard constructor.
     * @param JWT $jwt
     * @param Request $request
     */
    public function __construct(JWT $jwt, Request $request)
    {
        $this->jwt = $jwt;
        $this->request = $request;
    }

    /**
     * 获取当前认证用户。
     *
     * @return \Illuminate\Contracts\Auth\Authenticatable|null
     */
    public function user()
    {
        if (!is_null($this->user)) {
            return $this->user;
        }

        // 尝试从请求中获取并验证JWT
        if ($this->jwt->setRequest($this->request)->getToken() && $this->jwt->check()) {
            // JWT 验证成功,从载荷中获取用户信息
            $payload = $this->jwt->payload();
            $id = $payload->get('sub'); // 通常 'sub' 声明包含用户唯一标识符

            // 创建一个临时的用户实例。
            // 如果您的用户模型需要更多数据,可以从 $payload 中提取并设置。
            $this->user = new User();
            $this->user->id = $id;
            // 示例:从JWT载荷中设置其他用户属性
            // $this->user->name = $payload->get('name');
            // $this->user->email = $payload->get('email');

            // 可以在此处添加权限(scopes)验证逻辑
            // $scopes = $payload->get('scope', []);
            // if (!in_array('your_required_scope', explode(' ', $scopes))) {
            //     return null; // 权限不足
            // }

            return $this->user;
        }

        return null;
    }

    /**
     * 验证用户凭据(在本场景中不直接使用,JWT本身就是凭据)。
     *
     * @param array $credentials
     * @return bool
     */
    public function validate(array $credentials = [])
    {
        // 对于 JWT Guard,我们通常不通过 credentials 数组进行验证
        // 而是依赖于 user() 方法中对 JWT 的验证
        return false;
    }
}

请注意,上述 JWTGuard 中的 User 模型是一个简单的实现,它需要实现 Illuminate\Contracts\Auth\Authenticatable 接口。如果您没有对应的 Eloquent 模型,可以创建一个简单的类:

<?php

namespace App\Models;

use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
use Illuminate\Database\Eloquent\Model; // 如果您想利用Eloquent特性,可以继承Model

class User extends Model implements AuthenticatableContract
{
    protected $guarded = []; // 允许批量赋值所有字段

    /**
     * 获取用户身份标识符。
     *
     * @return mixed
     */
    public function getAuthIdentifier()
    {
        return $this->id;
    }

    /**
     * 获取用户身份标识符的名称。
     *
     * @return string
     */
    public function getAuthIdentifierName()
    {
        return 'id';
    }

    /**
     * 获取用于存储用户“记住我”令牌的名称。
     *
     * @return string
     */
    public function getRememberTokenName()
    {
        return ''; // 不使用记住我功能
    }

    /**
     * 获取用户“记住我”令牌。
     *
     * @return string|null
     */
    public function getRememberToken()
    {
        return null;
    }

    /**
     * 设置用户“记住我”令牌。
     *
     * @param string $value
     * @return void
     */
    public function setRememberToken($value)
    {
        // 不使用记住我功能
    }

    /**
     * 获取用户密码。
     *
     * @return string
     */
    public function getAuthPassword()
    {
        return ''; // 不通过密码认证
    }
}

3. 注册自定义 Guard

完成 JWTGuard 的实现后,需要在 AuthServiceProvider 中注册它,并更新 config/auth.php。

DoMore.ai
DoMore.ai

DoMore.ai 是一个个性化的 AI 工具目录

下载

在 AuthServiceProvider 中注册:

// app/Providers/AuthServiceProvider.php

namespace App\Providers;

use App\Guard\JWTGuard; // 引入自定义Guard
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Gate;

class AuthServiceProvider extends ServiceProvider
{
    /**
     * The policy mappings for the application.
     *
     * @var array
     */
    protected $policies = [
        // 'App\Models\Model' => 'App\Policies\ModelPolicy',
    ];

    /**
     * Register any authentication / authorization services.
     *
     * @return void
     */
    public function boot()
    {
        $this->registerPolicies();

        // 扩展认证系统,注册名为 'jwt-auth' 的 Guard
        $this->app['auth']->extend(
            'jwt-auth',
            function ($app, $name, array $config) {
                $guard = new JWTGuard(
                    $app['tymon.jwt'], // 注入 tymon/jwt-auth 的 JWT 实例
                    $app['request']    // 注入当前请求实例
                );

                // 确保在请求刷新时,Guard 也能更新其内部的请求实例
                $app->refresh('request', $guard, 'setRequest');

                return $guard;
            }
        );
    }
}

在 config/auth.php 中配置:

// config/auth.php

return [
    'defaults' => [
        'guard' => 'web', // 可以保持为 web,或根据您的应用默认需求修改
        'passwords' => 'users',
    ],

    'guards' => [
        'web' => [
            'driver' => 'session',
            'provider' => 'users',
        ],
        // ... 其他 guards

        'jwt' => [ // 注册一个新的名为 'jwt' 的 guard
            'driver' => 'jwt-auth', // 使用我们刚刚注册的 driver
            'provider' => 'users',  // 指定用户提供者,尽管我们使用自定义User模型,但仍需指定
        ],
    ],

    'providers' => [
        'users' => [
            'driver' => 'eloquent',
            'model' => App\Models\User::class, // 指定您的用户模型
        ],
        // ...
    ],
    // ...
];

4. 在路由中使用 Guard

现在,您可以通过 Laravel 的认证中间件来保护路由了。

// routes/api.php 或其他路由文件

use Illuminate\Support\Facades\Route;
use Illuminate\Support\Facades\Auth;

Route::middleware('auth:jwt')->get('/user', function () {
    // 只有通过 'jwt' Guard 认证的请求才能访问此路由
    return Auth::user();
});

Route::middleware('auth:jwt')->get('/protected-resource', function () {
    // ... 处理受保护的资源
    return response()->json(['message' => '访问受保护资源成功!']);
});

当客户端向 /user 或 /protected-resource 发送请求时,必须在 Authorization 头中包含有效的 Bearer JWT。

关键注意事项与最佳实践

  1. JWKS 动态获取与缓存

    • 在生产环境中,强烈建议实现一个机制来动态地从 IdP 的 JWKS URL 获取公钥。JWKS 包含一个或多个 JSON Web Key (JWK) 对象,每个对象都有一个 kid (Key ID)。
    • JWT 头部也会包含一个 kid,您应该使用这个 kid 来匹配 JWKS 中相应的公钥。
    • 获取到的公钥应该被缓存起来(例如使用 Laravel 的 Cache 系统),并定期刷新,以应对 IdP 的密钥轮换策略。
    • 一个简单的实现可能涉及一个服务类,它负责:
      • 从 JWKS URL 获取所有 JWK。
      • 根据 JWT 头部中的 kid 查找匹配的 JWK。
      • 将 JWK 转换为 OpenSSL 可用的 .pem 格式。
      • 缓存转换后的公钥。
  2. 声明验证

    • tymondesigns/jwt-auth 默认会验证一些标准声明,如 exp(过期时间)、nbf(生效时间)和 iat(签发时间)。
    • 您可能还需要验证 iss(签发者)和 aud(受众)声明,以确保 JWT 是由预期的 IdP 签发并针对您的服务。这些可以在 config/jwt.php 中配置。
  3. Scope 验证

    • 在 JWTGuard 的 user() 方法中,您可以从 payload()->get('scope') 获取到权限范围。
    • 通常,scope 声明是一个空格分隔的字符串或一个字符串数组。
    • 您可以将其解析并与当前路由或资源所需的权限进行比较。如果权限不足,则返回 null,阻止用户认证。
    • 对于更复杂的权限管理,可以考虑创建专门的中间件来处理 scope 验证,这样可以将认证和授权逻辑分离。
  4. 错误处理

    • 当 JWT 验证失败(例如签名无效、过期、缺少必要声明)时,tymondesigns/jwt-auth 会抛出异常(如 TokenExpiredException, TokenInvalidException)。
    • 您应该在 app/Exceptions/Handler.php 中捕获这些异常,并返回适当的 HTTP 错误响应(例如 401 Unauthorized)。
  5. 用户模型

    • 本教程中的 App\Models\User 是一个简单的实现。在实际应用中,您可能需要将其与数据库中的用户记录关联起来,或者从 JWT 载荷中提取更多信息来构建一个更完整的用户对象。
    • 关键是它必须实现 Illuminate\Contracts\Auth\Authenticatable 接口。

总结

通过以上步骤,您已经成功地在 Laravel 应用中搭建了一个健壮的第三方 JWT 验证机制。通过自定义 Guard,我们能够灵活地集成 tymondesigns/jwt-auth 的验证能力,并结合 JWKS 公钥配置,确保了来自外部身份提供商的访问令牌的有效性和安全性。记住,在生产环境中,动态获取和缓存 JWKS 公钥、以及全面的声明和权限验证是构建安全 API 的关键。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
laravel组件介绍
laravel组件介绍

laravel 提供了丰富的组件,包括身份验证、模板引擎、缓存、命令行工具、数据库交互、对象关系映射器、事件处理、文件操作、电子邮件发送、队列管理和数据验证。想了解更多laravel的相关内容,可以阅读本专题下面的文章。

338

2024.04.09

laravel中间件介绍
laravel中间件介绍

laravel 中间件分为五种类型:全局、路由、组、终止和自定。想了解更多laravel中间件的相关内容,可以阅读本专题下面的文章。

290

2024.04.09

laravel使用的设计模式有哪些
laravel使用的设计模式有哪些

laravel使用的设计模式有:1、单例模式;2、工厂方法模式;3、建造者模式;4、适配器模式;5、装饰器模式;6、策略模式;7、观察者模式。想了解更多laravel的相关内容,可以阅读本专题下面的文章。

708

2024.04.09

thinkphp和laravel哪个简单
thinkphp和laravel哪个简单

对于初学者来说,laravel 的入门门槛较低,更易上手,原因包括:1. 更简单的安装和配置;2. 丰富的文档和社区支持;3. 简洁易懂的语法和 api;4. 平缓的学习曲线。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

384

2024.04.10

laravel入门教程
laravel入门教程

本专题整合了laravel入门教程,想了解更多详细内容,请阅读专题下面的文章。

133

2025.08.05

laravel实战教程
laravel实战教程

本专题整合了laravel实战教程,阅读专题下面的文章了解更多详细内容。

82

2025.08.05

laravel面试题
laravel面试题

本专题整合了laravel面试题相关内容,阅读专题下面的文章了解更多详细内容。

75

2025.08.05

什么是中间件
什么是中间件

中间件是一种软件组件,充当不兼容组件之间的桥梁,提供额外服务,例如集成异构系统、提供常用服务、提高应用程序性能,以及简化应用程序开发。想了解更多中间件的相关内容,可以阅读本专题下面的文章。

181

2024.05.11

Swift iOS架构设计与MVVM模式实战
Swift iOS架构设计与MVVM模式实战

本专题聚焦 Swift 在 iOS 应用架构设计中的实践,系统讲解 MVVM 模式的核心思想、数据绑定机制、模块拆分策略以及组件化开发方法。内容涵盖网络层封装、状态管理、依赖注入与性能优化技巧。通过完整项目案例,帮助开发者构建结构清晰、可维护性强的 iOS 应用架构体系。

3

2026.03.03

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
PHP课程
PHP课程

共137课时 | 12.9万人学习

JavaScript ES5基础线上课程教学
JavaScript ES5基础线上课程教学

共6课时 | 11.3万人学习

PHP新手语法线上课程教学
PHP新手语法线上课程教学

共13课时 | 1.0万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号