0

0

Symfony 5.3 中使用 JWT 实现 API 认证与访问控制

碧海醫心

碧海醫心

发布时间:2025-07-19 14:34:01

|

659人浏览过

|

来源于php中文网

原创

symfony 5.3 中使用 jwt 实现 api 认证与访问控制

本文详细介绍了如何在 Symfony 5.3 应用中通过 JSON Web Token (JWT) 实现无状态 API 认证与访问控制。内容涵盖了 JWT 的生成、安全配置文件的设置(特别是 firewalls 和 access_control),以及自定义 JWT 认证器 (JwtAuthenticator) 的实现。核心在于正确配置 access_control 规则,以确保受保护的 API 路由在缺少有效 JWT 时被拒绝访问,从而有效保障 API 安全。

1. JWT 认证流程概述

在构建无状态 API 时,JWT 是一种流行的认证机制。其基本流程如下:

  1. 用户登录: 客户端向认证接口(如 /authenticate)发送用户名和密码。
  2. JWT 生成: 服务器验证凭据,如果有效,则生成一个包含用户身份信息的 JWT,并将其返回给客户端。
  3. 受保护资源访问: 客户端在后续请求受保护的 API 资源时,将 JWT 放入 Authorization 请求头中(通常以 Bearer 方案)。
  4. JWT 验证: 服务器接收请求后,通过自定义的认证器验证 JWT 的有效性(签名、过期时间等),并从中提取用户身份。
  5. 访问控制: Symfony 的安全组件根据验证后的用户身份和配置的访问控制规则,决定是否允许用户访问请求的资源。

2. 核心组件实现

为了在 Symfony 5.3 中实现上述流程,我们需要配置 security.yaml 文件,并创建一个自定义的 JWT 认证器。

2.1 JWT 的生成与返回

在认证控制器(例如 AuthController)中,当用户成功登录后,我们使用 Firebase\JWT\JWT 库来生成一个 JWT,并将其作为 JSON 响应返回给客户端。

 'your-api-domain.com', // 签发者
            'aud' => 'your-api-client',     // 受众
            'iat' => time(),                // 签发时间
            'exp' => time() + 3600,         // 过期时间(1小时)
            'sub' => $userId                // 主题:用户ID
        ];

        // 从参数中获取 JWT 密钥
        $jwtSecret = $params->get('jwt_secret');

        // 使用 HS256 算法生成 JWT
        $jwt = JWT::encode($payload, $jwtSecret, 'HS256');

        $body = [
            'auth_token' => $jwt,
        ];

        return new JsonResponse($body, 201);
    }
}

请确保您的 services.yaml 或 parameters.yaml 中定义了 jwt_secret 参数,例如:

# config/services.yaml 或 config/packages/parameters.yaml
parameters:
    jwt_secret: '%env(JWT_SECRET)%' # 建议从环境变量获取

2.2 Symfony 安全配置 (security.yaml)

security.yaml 是 Symfony 安全配置的核心,它定义了防火墙、认证提供者和访问控制规则。正确配置此文件是实现 JWT 认证的关键。

QIMI奇觅
QIMI奇觅

美图推出的游戏行业广告AI制作与投放一体化平台

下载
# config/packages/security.yaml
security:
    enable_authenticator_manager: true # Symfony 5.3 推荐使用此管理器

    password_hashers:
        Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: 'auto'

    encoders: # 兼容旧版 Symfony 认证器
        App\Entity\ATblUsers:
            algorithm: bcrypt

    providers:
        # 这里可以使用您的用户提供者,例如从数据库加载用户
        # 示例中使用内存用户,实际应用中应替换为您的用户实体提供者
        users_in_memory: { memory: null }
        # 例如:
        # app_user_provider:
        #     entity: { class: App\Entity\ATblUsers, property: email }

    firewalls:
        dev: # 开发环境调试防火墙,不进行安全检查
            pattern: ^/(_(profiler|wdt)|css|images|js)/
            security: false

        main: # 主防火墙,处理大部分API请求
            guard: # Symfony 5.3 仍然支持 Guard 认证器
                authenticators:
                    - App\Security\JwtAuthenticator # 注册我们的自定义认证器
            lazy: true # 惰性加载用户,按需加载
            provider: users_in_memory # 指定用户提供者
            stateless: true # 声明此防火墙是无状态的,不使用会话

    access_control:
        # 允许所有用户访问认证接口,无需任何凭证
        - { path: ^/authenticate, roles: PUBLIC_ACCESS }
        # 对所有其他路径强制要求完全认证(即必须提供有效JWT)
        - { path: ^/, roles: IS_AUTHENTICATED_FULLY }

关键配置点解析:

  • firewalls.main.guard.authenticators: 这里指定了我们自定义的 JwtAuthenticator 类,让 Symfony 知道如何处理 JWT 认证。
  • firewalls.main.stateless: true: 这是 API 认证的关键。它告诉 Symfony 此防火墙不应使用会话来维护用户状态,每次请求都必须提供认证凭证。
  • access_control: 这是解决原始问题(未认证也能访问受保护路由)的核心。
    • { path: ^/authenticate, roles: PUBLIC_ACCESS }: 这条规则允许任何人访问 /authenticate 路径,因为它是用于获取 JWT 的登录接口。PUBLIC_ACCESS 是一个特殊的角色,表示无需认证。
    • { path: ^/, roles: IS_AUTHENTICATED_FULLY }: 这条规则是通用规则,它要求所有以 / 开头的路径(即除了 /authenticate 之外的所有路径,因为 access_control 是按顺序匹配的)都必须经过“完全认证”。IS_AUTHENTICATED_FULLY 意味着用户不仅要被认证,而且不能是“记住我”认证的用户。

2.3 自定义 JWT 认证器 (JwtAuthenticator.php)

JwtAuthenticator 继承自 AbstractGuardAuthenticator,它负责从请求中提取 JWT、验证其有效性并加载相应的用户。

em = $em;
        $this->params = $params;
    }

    /**
     * 当认证失败时,此方法会被调用,返回一个 JSON 响应。
     */
    public function start(Request $request, AuthenticationException $authException = null): JsonResponse
    {
        $body = [
            'message' => 'Authentication Required',
        ];
        return new JsonResponse($body, Response::HTTP_UNAUTHORIZED);
    }

    /**
     * 判断当前请求是否支持此认证器。
     * 如果请求头中包含 'Authorization',则表示支持。
     */
    public function supports(Request $request): bool
    {
        return $request->headers->has('Authorization');
    }

    /**
     * 从请求中获取认证凭证(JWT)。
     */
    public function getCredentials(Request $request)
    {
        // 提取 Bearer Token
        $authorizationHeader = $request->headers->get('Authorization');
        if (str_starts_with($authorizationHeader, 'Bearer ')) {
            return str_replace('Bearer ', '', $authorizationHeader);
        }
        return null; // 如果不是 Bearer token,返回 null
    }

    /**
     * 根据凭证加载用户。
     * 在这里解码 JWT 并通过其中的 'sub'(用户ID)查找用户。
     */
    public function getUser($credentials, UserProviderInterface $userProvider)
    {
        if (null === $credentials) {
            return null;
        }

        try {
            // 获取 JWT 密钥
            $jwtSecret = $this->params->get('jwt_secret');
            // 解码 JWT
            $jwt = (array) JWT::decode($credentials, new \Firebase\JWT\Key($jwtSecret, 'HS256'));

            // 假设 'sub' 字段存储用户ID
            if (!isset($jwt['sub'])) {
                throw new AuthenticationException('JWT payload does not contain user ID (sub).');
            }

            // 从数据库中查找用户实体
            // 假设您的用户实体类为 App\Entity\ATblUsers
            return $this->em->getRepository('App\Entity\ATblUsers')->find($jwt['sub']);
        } catch (\Exception $exception) {
            // JWT 解码失败(如签名无效、过期等)或用户不存在
            throw new AuthenticationException($exception->getMessage());
        }
    }

    /**
     * 检查凭证是否有效。
     * 对于 JWT,通常在 getUser() 中已经完成了所有验证,此方法可以留空或进行额外检查。
     */
    public function checkCredentials($credentials, UserInterface $user): bool
    {
        // JWT 的有效性(签名、过期)已在 getUser() 中验证
        // 这里可以进行额外的用户状态检查,例如用户是否被禁用
        return true;
    }

    /**
     * 认证失败时被调用,返回 JSON 错误响应。
     */
    public function onAuthenticationFailure(Request $request, AuthenticationException $exception): JsonResponse
    {
        return new JsonResponse([
            'message' => 'Authentication Failed: ' . $exception->getMessage()
        ], Response::HTTP_UNAUTHORIZED);
    }

    /**
     * 认证成功时被调用。
     * 对于无状态 API,通常不返回任何内容。
     */
    public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $providerKey)
    {
        return null; // 继续处理请求
    }

    /**
     * 是否支持记住我功能。对于无状态 API,通常为 false。
     */
    public function supportsRememberMe(): bool
    {
        return false;
    }
}

注意: 在 getUser 方法中,JWT::decode 的第二个参数在 firebase/php-jwt 6.0+ 版本中已改为 \Firebase\JWT\Key 对象。请根据您安装的 firebase/php-jwt 版本进行调整。上述代码已更新为新版本兼容写法。

3. 注意事项与最佳实践

  • access_control 的匹配顺序: access_control 规则是按顺序匹配的,一旦匹配到第一条规则,后续规则将不再检查。因此,更具体的路径规则应放在更通用的规则之前。例如,/authenticate 必须放在 / 之前。
  • JWT 密钥管理: 用于签名和验证 JWT 的密钥 (jwt_secret) 必须保密。建议通过环境变量 (%env(JWT_SECRET)%) 来管理,而不是直接硬编码在配置文件中。
  • 用户提供者: 示例中使用了 users_in_memory,但在实际应用中,您应该配置一个能够从数据库或其他持久化存储中加载用户的提供者(例如,通过 Doctrine ORM 加载 App\Entity\ATblUsers)。
  • 错误处理: JwtAuthenticator 中的 onAuthenticationFailure 方法可以捕获认证失败时的异常,并返回有意义的错误信息给客户端。
  • Symfony 5.3+ 与 Guard 认证器: 尽管 Symfony 5.3 仍然支持 Guard 认证器,但 Symfony 5.4 及更高版本推荐使用新的 AuthenticatorManager 和 AuthenticatorInterface。如果您计划升级到更高版本,可能需要考虑将 Guard 认证器迁移到新的认证系统。

4. 总结

通过以上配置和自定义认证器的实现,我们成功地在 Symfony 5.3 应用中建立了基于 JWT 的无状态 API 认证系统。核心在于通过 security.yaml 中的 firewalls 和 access_control 定义认证机制和访问权限,并利用 JwtAuthenticator 负责 JWT 的解析和用户加载。这确保了只有持有有效 JWT 的请求才能访问受保护的 API 资源,从而提升了 API 的安全性。

热门AI工具

更多
DeepSeek
DeepSeek

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

豆包大模型
豆包大模型

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

通义千问
通义千问

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

腾讯元宝
腾讯元宝

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

文心一言
文心一言

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

讯飞写作
讯飞写作

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

即梦AI
即梦AI

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

ChatGPT
ChatGPT

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

相关专题

更多
PHP Symfony框架
PHP Symfony框架

本专题专注于PHP主流框架Symfony的学习与应用,系统讲解路由与控制器、依赖注入、ORM数据操作、模板引擎、表单与验证、安全认证及API开发等核心内容。通过企业管理系统、内容管理平台与电商后台等实战案例,帮助学员全面掌握Symfony在企业级应用开发中的实践技能。

78

2025.09.11

json数据格式
json数据格式

JSON是一种轻量级的数据交换格式。本专题为大家带来json数据格式相关文章,帮助大家解决问题。

419

2023.08.07

json是什么
json是什么

JSON是一种轻量级的数据交换格式,具有简洁、易读、跨平台和语言的特点,JSON数据是通过键值对的方式进行组织,其中键是字符串,值可以是字符串、数值、布尔值、数组、对象或者null,在Web开发、数据交换和配置文件等方面得到广泛应用。本专题为大家提供json相关的文章、下载、课程内容,供大家免费下载体验。

535

2023.08.23

jquery怎么操作json
jquery怎么操作json

操作的方法有:1、“$.parseJSON(jsonString)”2、“$.getJSON(url, data, success)”;3、“$.each(obj, callback)”;4、“$.ajax()”。更多jquery怎么操作json的详细内容,可以访问本专题下面的文章。

311

2023.10.13

go语言处理json数据方法
go语言处理json数据方法

本专题整合了go语言中处理json数据方法,阅读专题下面的文章了解更多详细内容。

77

2025.09.10

登录token无效
登录token无效

登录token无效解决方法:1、检查token的有效期限,如果token已经过期,需要重新获取一个新的token;2、检查token的签名,如果签名不正确,需要重新获取一个新的token;3、检查密钥的正确性,如果密钥不正确,需要重新获取一个新的token;4、使用HTTPS协议传输token,建议使用HTTPS协议进行传输 ;5、使用双因素认证,双因素认证可以提高账户的安全性。

6169

2023.09.14

登录token无效怎么办
登录token无效怎么办

登录token无效的解决办法有检查Token是否过期、检查Token是否正确、检查Token是否被篡改、检查Token是否与用户匹配、清除缓存或Cookie、检查网络连接和服务器状态、重新登录或请求新的Token、联系技术支持或开发人员等。本专题为大家提供token相关的文章、下载、课程内容,供大家免费下载体验。

818

2023.09.14

token怎么获取
token怎么获取

获取token值的方法:1、小程序调用“wx.login()”获取 临时登录凭证code,并回传到开发者服务器;2、开发者服务器以code换取,用户唯一标识openid和会话密钥“session_key”。想了解更详细的内容,可以阅读本专题下面的文章。

1066

2023.12.21

clawdbot ai使用教程 保姆级clawdbot部署安装手册
clawdbot ai使用教程 保姆级clawdbot部署安装手册

Clawdbot是一个“有灵魂”的AI助手,可以帮用户清空收件箱、发送电子邮件、管理日历、办理航班值机等等,并且可以接入用户常用的任何聊天APP,所有的操作均可通过WhatsApp、Telegram等平台完成,用户只需通过对话,就能操控设备自动执行各类任务。

11

2026.01.29

热门下载

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

精品课程

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

共14课时 | 0.8万人学习

Bootstrap 5教程
Bootstrap 5教程

共46课时 | 3.1万人学习

CSS教程
CSS教程

共754课时 | 24.8万人学习

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

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