ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

Yii 2 RESTful API 认证(Authentication)完整指南:无状态访问令牌、authenticator 行为与身份实现

Yii 2 RESTful API 认证(Authentication)完整指南:无状态访问令牌、authenticator 行为与身份实现 Yii 2 RESTful API 认证Authentication完整指南无状态访问令牌、authenticator 行为与身份实现【免费下载链接】yii2Yii 2: The Fast, Secure and Professional PHP Framework项目地址: https://gitcode.com/gh_mirrors/yi/yii2导读本文围绕 Yii 2 框架中 RESTful API 的认证机制展开核心解决如何在无会话stateless的 API 场景下安全识别请求者身份这一工程问题。你将掌握三类主流令牌传递方式HTTP Basic Auth、查询参数、OAuth 2 Bearer Token在 Yii 2 中的配置方法理解authenticator行为filter的底层执行链路并能在自己的User身份类中正确实现findIdentityByAccessToken()。读完本文即可为 API 控制器接入完整认证流程并在此基础上衔接授权authorization检查。一、为什么 RESTful API 需要每个请求自带凭证与传统的 Web 应用不同RESTful API 通常是无状态stateless的服务端不依赖会话session与 Cookie 来维持用户登录状态。这意味着服务端无法通过会话判断当前请求是谁发起的因此每一个请求都必须携带某种形式的认证凭证。最常见的做法是客户端在每次请求中附带一个秘密访问令牌access token。由于该令牌足以唯一标识并认证一个用户令牌一旦泄露即等同账号泄露所以文档中特别强调API 请求必须始终通过 HTTPS 发送以防止中间人man-in-the-middle, MitM攻击。这是使用令牌认证的底线安全要求任何接入方案都不应绕过。二、三种访问令牌的发送方式Yii 2 官方对这三种方式的定位与适用场景如下发送方式令牌位置适用场景与注意事项HTTP Basic Auth令牌作为用户名发送密码字段被忽略仅当令牌能在 API 消费方安全存储时使用例如运行在服务器上的后台程序查询参数URL 查询串如https://example.com/users?access-tokenxxxxxxxx大多数 Web 服务器会把查询参数记录进访问日志因此主要仅用于无法在 HTTP 头中携带令牌的JSONP请求OAuth 2通过 HTTP Bearer Token见 RFC 6750发送令牌由授权服务器authorization server颁发客户端再将其带给 API 服务器遵循 OAuth2 协议Yii 2 对以上三种方式都提供了开箱即用的支持同时允许开发者自定义新的认证方法。三、启用认证的三个步骤要在 Yii 2 中为 API 开启认证官方文档给出了三个步骤配置user应用组件将 [[yii\web\User::enableSession|enableSession]] 设为false对于英文原版指南还建议将 [[yii\web\User::loginUrl|loginUrl]] 设为null使未授权访问返回 HTTP 403 而不是跳转登录页。指定认证方法在 REST 控制器类中通过配置authenticator行为behavior声明打算使用的认证方式。实现身份类方法在 [[yii\web\User::identityClass|身份类]] 中实现 [[yii\web\IdentityInterface::findIdentityByAccessToken()]]。其中第 1 步不是强制的但强烈建议执行——因为 RESTful API 本就不应保存客户端状态。当enableSession为false时用户的认证状态不会通过会话在多次请求间持久化取而代之的是每个请求都会重新执行一次认证这正是第 2、3 步所完成的工作。从源码看enableSession默认值为trueloginUrl默认值为[site/login]参见 framework/web/User.php。不关闭会话时User组件会尝试通过 session/cookie 读取身份信息见getIdentity()中enableSession $autoRenew的分支逻辑这与无状态 API 的语义相悖。3.1 在模块中关闭会话Tip如果你的 RESTful API 以**应用application形式开发可在应用配置中设置user组件的enableSession如果以模块module**形式开发则可在模块的init()方法中加入如下一行public function init() { parent::init(); \Yii::$app-user-enableSession false; }四、配置 authenticator 行为从单一方式到组合方式authenticator是挂在 REST 控制器上的一个行为。框架内置的 REST 基类 framework/rest/Controller.php 在behaviors()中已经默认注册了authenticator使用空的CompositeAuth以及contentNegotiator、verbFilter、rateLimiter等行为因此你在自己的控制器中重写behaviors()时通常先调用parent::behaviors()再覆盖authenticator键。4.1 仅使用 HTTP Basic Authuse yii\filters\auth\HttpBasicAuth; public function behaviors() { $behaviors parent::behaviors(); $behaviors[authenticator] [ class HttpBasicAuth::class, ]; return $behaviors; }关于HttpBasicAuth的底层实现framework/filters/auth/HttpBasicAuth.php有几点值得注意默认realm为api认证失败时challenge()会向响应头写入WWW-Authenticate: Basic realmapi。默认情况下它从请求中读取 Basic 凭证$request-getAuthCredentials()把用户名当作访问令牌调用Yii::$app-user-loginByAccessToken($username, get_class($this))密码被忽略——这正是令牌作为用户名的落地实现。若想改用用户名 密码的传统校验方式可传入auth回调例如class HttpBasicAuth::class, auth function ($username, $password) { $user User::find()-where([username $username])-one(); if ($user $user-validatePassword($password)) { return $user; } return null; },如果认证行为不符合预期请确认 Web 服务器确实把用户名/密码传递给了$_SERVER[PHP_AUTH_USER]与$_SERVER[PHP_AUTH_PW]。在 Apache PHP-CGI 环境下可能需要在.htaccess中加入RewriteRule .* - [EHTTP_AUTHORIZATION:%{HTTP:Authorization},L]4.2 同时支持全部三种方式CompositeAuth若希望 API 同时接受 HTTP Basic Auth、HTTP Bearer Token 与查询参数令牌可使用CompositeAuthuse yii\filters\auth\CompositeAuth; use yii\filters\auth\HttpBasicAuth; use yii\filters\auth\HttpBearerAuth; use yii\filters\auth\QueryParamAuth; public function behaviors() { $behaviors parent::behaviors(); $behaviors[authenticator] [ class CompositeAuth::class, authMethods [ HttpBasicAuth::class, HttpBearerAuth::class, QueryParamAuth::class, ], ]; return $behaviors; }authMethods数组中的每个元素必须是认证方法类名或一个配置数组例如可写成[class HttpBearerAuth::class, realm admin]。CompositeAuth的源码framework/filters/auth/CompositeAuth.php会按顺序遍历这些方法逐个调用其authenticate()一旦某个方法返回了非空的 identity 就立即返回全部方法都失败才返回null并触发失败处理。若authMethods为空beforeAction()直接放行即不执行任何认证。4.3 三种内置认证过滤器的源码级细节过滤器关键属性认证逻辑源码依据HttpBasicAuthrealm apiauth回调用户名作为令牌loginByAccessToken($username, ...)失败时写入WWW-Authenticate: Basic realm...HttpBearerAuthheader Authorizationpattern /^Bearer\s(.*?)$/realm api从Authorization头中按正则提取 Bearer 令牌再loginByAccessToken()失败时写WWW-Authenticate: Bearer realm...见 framework/filters/auth/HttpBearerAuth.phpQueryParamAuthtokenParam access-token从$request-get($this-tokenParam)读取令牌令牌存在但无效时触发失败见 framework/filters/auth/QueryParamAuth.php此外基类AuthMethodframework/filters/auth/AuthMethod.php还提供了一个实用的optional属性自 2.0.7 起支持通配符如site/*声明为 optional 的动作认证失败不会导致报错可用于公开可见、但对已登录用户返回更多数据的接口。认证失败默认抛出UnauthorizedHttpExceptionHTTP 401错误信息为Your request was made with invalid credentials.。五、实现 findIdentityByAccessToken()findIdentityByAccessToken()的实现完全取决于应用自身的数据模型。以最简单的场景为例——每个用户只有一个访问令牌可将令牌存放在用户表的access_token字段中实现如下use yii\db\ActiveRecord; use yii\web\IdentityInterface; class User extends ActiveRecord implements IdentityInterface { public static function findIdentityByAccessToken($token, $type null) { return static::findOne([access_token $token]); } }从框架侧看framework/web/User.phpUser::loginByAccessToken($token, $type)会先调用身份类上的findIdentityByAccessToken($token, $type)找到身份后调用login($identity)完成登录任一环节失败都返回null。其中$type参数由过滤器传入例如HttpBearerAuth会传入自身的类名可据此在同一张表内区分不同认证方式签发的令牌。需要留意上面是文档中的简化示例。真实项目中令牌通常需要加密存储、支持多令牌/令牌吊销并建议采用token_type 哈希值组合查询而不是直接以明文列等值匹配——这些属于应用层安全设计可结合 docs/guide-ru/security-passwords.md 中关于密码/机密信息处理的建议实施。六、认证在请求生命周期中的位置与失败响应完成上述配置后每个到达 API 的请求都会在目标控制器的beforeAction()阶段尝试认证用户AuthMethod::beforeAction()内部依次执行authenticate()、challenge()与handleFailure()见 framework/filters/auth/AuthMethod.php。认证成功控制器继续执行后续检查如频率限制 rate limiting、授权 authorization然后运行具体 action。已认证用户的信息可通过Yii::$app-user-identity获取。认证失败返回HTTP 401状态码并附带相应头信息——例如 HTTP Basic Auth 会返回WWW-Authenticate头以便客户端浏览器/HTTP 客户端据此发起质询challenge。从 REST 控制器的整体请求处理周期看见 framework/rest/Controller.php 的类注释依次是解析响应格式ContentNegotiator→ 校验请求方法VerbFilter→认证用户AuthInterface→ 频率限制RateLimiter→ 序列化响应数据。认证恰好位于方法校验之后、限流与授权之前。七、认证之后授权Authorization认证只解决你是谁的问题接下来通常还要确认你有没有权限执行该操作即授权authorization其完整体系参见 docs/guide-ru/security-authorization.mdRBAC、access 控制过滤器等。针对 REST 场景如果你的控制器继承自yii\rest\ActiveController可以重写checkAccess()方法来完成授权检查。该方法会被ActiveController内置的各个 actionindex、view、create、update、delete等在操作前后自动调用public function checkAccess($action, $model null, $params []) { // 例如仅允许资源所有者修改自己的记录 if (in_array($action, [update, delete]) $model-user_id ! \Yii::$app-user-id) { throw new \yii\web\ForbiddenHttpException(You are not allowed to perform this action.); } }基类中的默认实现为空方法见 framework/rest/ActiveController.php即默认不做任何限制抛出ForbiddenHttpExceptionHTTP 403即可拒绝访问。文档同时提示若loginUrl被设为null未授权访问将返回 HTTP 403 而非重定向到登录页这与 API 场景的语义一致。八、完整落地示例模块方式把以上内容组合成一个可在项目中直接参考的模块初始化片段namespace app\modules\api; use Yii; class Module extends \yii\base\Module { public function init() { parent::init(); // 关闭会话确保 API 完全无状态 Yii::$app-user-enableSession false; // 未授权时返回 403 而不是跳转登录页可选建议 Yii::$app-user-loginUrl null; } }控制器侧则按 4.1 / 4.2 的方式覆盖behaviors()注册authenticator并在身份类中实现findIdentityByAccessToken()。三者齐备后客户端即可分别通过Authorization: Basic base64(user:pass)、Authorization: Bearer token或?access-tokentoken访问受保护的 API 端点。九、常见问题与排查要点认证一直失败优先确认 Web 服务器是否正确传递了 PHP 认证变量见 4.1 的.htaccess提示其次确认身份类已实现findIdentityByAccessToken()且返回的对象正确实现了IdentityInterface。明明配置了认证却仍能匿名访问检查behaviors()是否覆盖了parent::behaviors()返回的authenticator键或authMethods是否为空空数组时CompositeAuth直接放行。想对部分公开接口放行使用AuthMethod::$optional属性支持site/*形式的通配符。HTTP 401 与 403 的区别凭证缺失/无效时返回 401UnauthorizedHttpException身份有效但无权限时返回 403ForbiddenHttpException二者由认证与授权两个阶段分别产生。相关资源指南原文俄语版docs/guide-ru/rest-authentication.md英文版见 docs/guide/rest-authentication.md认证过滤器实现framework/filters/auth/AuthMethod、CompositeAuth、HttpBasicAuth、HttpBearerAuth、HttpHeaderAuth、QueryParamAuthREST 控制器与内置 actionframework/rest/Controller.php、framework/rest/ActiveController.phpuser组件与令牌登录逻辑framework/web/User.php身份接口定义framework/web/IdentityInterface.php授权进阶docs/guide-ru/security-authorization.md应用组件概念docs/guide-ru/structure-application-components.md【免费下载链接】yii2Yii 2: The Fast, Secure and Professional PHP Framework项目地址: https://gitcode.com/gh_mirrors/yi/yii2创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表