You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 04:17:57