如何用OAuth2保护Zend3+bshaffer/oauth2-server-php的REST API接口
嘿,我刚好有过用bshaffer/oauth2-server-php搭配Zend Framework 3保护REST API的实战经验,给你分享两种靠谱的实现方式,帮你把那些需要认证的接口牢牢锁起来,只允许带有效Authorization Bearer令牌的请求访问:
方式一:用PSR-7中间件(推荐,支持全局或路由级保护)
这种方式适合批量保护一组路由(比如所有/api开头的接口),或者给特定路由单独加认证,逻辑统一且易维护。
步骤1:创建OAuth2验证中间件
先写一个中间件类,负责提取请求里的Bearer令牌,并用oauth2-server验证其有效性:
// src/Middleware/OAuth2Middleware.php namespace App\Middleware; use OAuth2\Server; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Server\MiddlewareInterface; use Psr\Http\Server\RequestHandlerInterface; use Zend\Diactoros\Response\JsonResponse; class OAuth2Middleware implements MiddlewareInterface { private $oauthServer; public function __construct(Server $oauthServer) { $this->oauthServer = $oauthServer; } public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface { // 验证资源请求(自动提取Authorization头里的Bearer令牌) if (!$this->oauthServer->verifyResourceRequest($request)) { // 验证失败,返回401错误 $response = $this->oauthServer->getResponse(); return new JsonResponse( ['error' => $response->getParameter('error'), 'error_description' => $response->getParameter('error_description')], $response->getStatusCode() ); } // 验证通过,继续处理请求 return $handler->handle($request); } }
步骤2:注册中间件到服务容器
在config/autoload/dependencies.global.php里,把中间件和你的OAuth2 Server实例注册到服务管理器:
return [ 'service_manager' => [ 'factories' => [ // 假设你已经有了OAuth2 Server的工厂类 OAuth2\Server::class => App\Factory\OAuth2ServerFactory::class, App\Middleware\OAuth2Middleware::class => App\Factory\OAuth2MiddlewareFactory::class, ], ], ];
(注:OAuth2MiddlewareFactory的作用就是从容器里取出OAuth2\Server实例,注入到中间件里,逻辑很简单,就是普通的工厂类)
步骤3:给目标路由绑定中间件
打开你的路由配置文件(比如module/Application/config/module.config.php),给需要保护的路由添加中间件:
'routes' => [ 'api-rest-myapp-getlist' => [ 'type' => \Zend\Router\Http\Literal::class, 'options' => [ 'route' => '/api/rest/myapp/GetList', 'defaults' => [ 'controller' => Application\Controller\RestController::class, 'action' => 'getList', ], ], 'middleware' => [ App\Middleware\OAuth2Middleware::class, // 这里加上认证中间件 ], 'may_terminate' => true, ], // 其他需要保护的路由也可以照此添加 ],
如果要保护所有/api开头的接口,可以用路由组来批量绑定中间件,更高效。
方式二:自定义控制器插件(适合零散控制器的场景)
如果只是部分控制器动作需要认证,用控制器插件会更灵活,在需要的地方手动调用验证逻辑即可。
步骤1:创建OAuth2认证插件
写一个控制器插件类,封装令牌验证逻辑:
// src/Controller/Plugin/OAuth2AuthPlugin.php namespace App\Controller\Plugin; use OAuth2\Server; use OAuth2\Exception; use Zend\Mvc\Controller\Plugin\AbstractPlugin; use Zend\Diactoros\Response\JsonResponse; class OAuth2AuthPlugin extends AbstractPlugin { private $oauthServer; public function __construct(Server $oauthServer) { $this->oauthServer = $oauthServer; } public function checkAuth() { $request = $this->getController()->getRequest(); if (!$this->oauthServer->verifyResourceRequest($request)) { $response = $this->oauthServer->getResponse(); throw new Exception( $response->getParameter('error_description'), $response->getStatusCode() ); } // 验证通过的话,还可以返回令牌的用户信息之类的 return $this->oauthServer->getAccessTokenData($request); } }
步骤2:注册插件到控制器插件管理器
在module/Application/config/module.config.php里添加插件配置:
'controller_plugins' => [ 'factories' => [ 'oauth2Auth' => App\Controller\Plugin\Factory\OAuth2AuthPluginFactory::class, ], ],
步骤3:在控制器动作里调用验证
在需要保护的动作里,调用插件的验证方法,失败则返回401:
// Application/Controller/RestController.php public function getListAction() { try { // 调用验证插件,通过的话可以拿到令牌数据 $tokenData = $this->oauth2Auth()->checkAuth(); } catch (Exception $e) { return new JsonResponse( ['error' => $e->getMessage()], $e->getCode() ); } // 这里写正常的业务逻辑,比如返回列表数据 return new JsonResponse(['data' => '你的列表数据']); }
一些关键注意点
- 确保你的
OAuth2\Server实例已经正确配置了资源服务器存储(比如AccessTokenStorage),因为验证令牌需要从存储中查询令牌的有效性、过期时间等信息。 - 如果你的API需要支持跨域(CORS),记得在响应里添加允许
Authorization头的配置,比如在中间件里加上:$response = $handler->handle($request); return $response->withHeader('Access-Control-Allow-Origin', '*') ->withHeader('Access-Control-Allow-Headers', 'Authorization, Content-Type'); - 中间件的执行顺序很重要:确保OAuth2中间件在路由匹配之后、控制器执行之前运行,这样才能正确解析路由对应的请求。
内容的提问来源于stack exchange,提问作者altralaser
相关产品推荐
相关产品推荐

