如何通过PHP OAuth1实现Canonical MAAS REST API身份验证?
PHP对接Canonical MAAS REST API的OAuth1身份验证方案
我之前也踩过这个坑——MAAS的API密钥是consumer_key:token:secret三部分格式,而大部分PHP OAuth1示例只处理consumer key/secret和token/secret这两对,很容易让人摸不着头脑。其实MAAS的OAuth1实现是HMAC-SHA1的特殊变体,核心点就是consumer secret为空字符串,抓住这个就能顺利搞定。
下面给你两种可行的实现方式:
方式一:手动构建OAuth1请求(无需第三方库)
如果不想引入额外依赖,手动处理签名逻辑反而更直观:
<?php // 替换为你的MAAS API密钥和服务器地址 $maasApiKey = 'your_maas_api_key_here'; $maasServerUrl = 'http://your-maas-server:5240/MAAS/api/2.0'; // 拆分API密钥的三个核心部分 list($consumerKey, $token, $tokenSecret) = explode(':', $maasApiKey); // 要请求的API端点,比如获取所有机器列表 $apiEndpoint = '/machines/'; $fullUrl = $maasServerUrl . $apiEndpoint; // 构建OAuth1基础参数,每个请求都要生成新的nonce和timestamp $oauthParams = [ 'oauth_consumer_key' => $consumerKey, 'oauth_token' => $token, 'oauth_signature_method' => 'HMAC-SHA1', 'oauth_timestamp' => time(), 'oauth_nonce' => uniqid('', true), // 生成唯一随机字符串 'oauth_version' => '1.0' ]; // 生成签名基字符串,必须严格按照RFC3986编码 $baseString = http_build_query($oauthParams, '', '&', PHP_QUERY_RFC3986); $baseString = 'GET&' . rawurlencode($fullUrl) . '&' . rawurlencode($baseString); // MAAS的consumer secret为空,所以签名密钥格式为 "&tokenSecret" $signingKey = '&' . rawurlencode($tokenSecret); // 生成HMAC-SHA1签名并base64编码 $signature = base64_encode(hash_hmac('sha1', $baseString, $signingKey, true)); $oauthParams['oauth_signature'] = $signature; // 构建Authorization请求头 $authHeader = 'OAuth ' . http_build_query($oauthParams, '', ', ', PHP_QUERY_RFC3986); // 发送GET请求 $ch = curl_init($fullUrl); curl_setopt($ch, CURLOPT_HTTPHEADER, [$authHeader]); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); // 如果是自签名证书的MAAS服务器,可临时跳过证书验证(生产环境不建议) // curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); // 处理响应 if ($httpCode === 200) { print_r(json_decode($response, true)); } else { echo "请求失败,状态码:$httpCode,响应内容:$response"; } ?>
关键注意事项:
- 签名密钥必须带开头的
&:OAuth1签名密钥规则是consumerSecret&tokenSecret,MAAS的consumer secret为空,所以必须保留&前缀 - 每个请求必须生成新的
oauth_nonce和oauth_timestamp,重复使用会导致签名验证失败 - 所有参数必须用
PHP_QUERY_RFC3986编码,避免特殊字符破坏签名逻辑
方式二:使用league/oauth1-client库(更规范)
如果项目已经用Composer管理依赖,推荐用league/oauth1-client封装OAuth1逻辑,只需自定义一个MAAS Server类即可:
1. 安装依赖
composer require league/oauth1-client
2. 实现自定义MAAS Server类
<?php use League\OAuth1\Client\Server\Server; use League\OAuth1\Client\Credentials\TokenCredentials; use Psr\Http\Message\ResponseInterface; class MAASServer extends Server { // MAAS不需要临时凭证、授权页面和token获取流程,直接返回空字符串 public function urlTemporaryCredentials(): string { return ''; } public function urlAuthorization(): string { return ''; } public function urlTokenCredentials(): string { return ''; } // 示例:获取当前用户信息的端点 public function urlUserDetails(): string { return $this->getConfig('base_url') . '/users/me/'; } // 解析用户信息(根据MAAS的响应结构调整) public function userDetails($data, TokenCredentials $tokenCredentials): object { return (object) [ 'id' => $data['id'], 'username' => $data['username'], 'email' => $data['email'] ]; } public function userUid($data, TokenCredentials $tokenCredentials): string { return (string) $data['id']; } public function userEmail($data, TokenCredentials $tokenCredentials): ?string { return $data['email'] ?? null; } public function userScreenName($data, TokenCredentials $tokenCredentials): string { return $data['username']; } // 指定签名方法为HMAC-SHA1 protected function signatureMethod() { return $this->createHmacSha1SignatureMethod(); } // 自定义请求方法,方便调用任意MAAS API端点 public function request(string $url, TokenCredentials $tokenCredentials, string $method = 'GET', array $parameters = []): ResponseInterface { $this->client->setBaseUrl(''); return $this->createRequest($method, $url, $tokenCredentials, $parameters); } }
3. 使用示例
<?php require 'vendor/autoload.php'; // 配置MAAS信息 $maasConfig = [ 'identifier' => 'your_consumer_key_here', // API密钥的第一部分 'secret' => '', // MAAS的consumer secret为空 'base_url' => 'http://your-maas-server:5240/MAAS/api/2.0' ]; // 初始化Server $maasServer = new MAASServer($maasConfig); // 构建Token凭证(API密钥的第二、第三部分) $tokenCredentials = new TokenCredentials(); $tokenCredentials->setIdentifier('your_token_here'); $tokenCredentials->setSecret('your_token_secret_here'); // 示例1:获取当前用户信息 try { $user = $maasServer->getUserDetails($tokenCredentials); echo "当前用户:{$user->username}({$user->email})"; } catch (Exception $e) { echo "获取用户信息失败:{$e->getMessage()}"; } // 示例2:获取所有机器列表 try { $response = $maasServer->request($maasConfig['base_url'] . '/machines/', $tokenCredentials); $machines = json_decode($response->getBody(), true); print_r($machines); } catch (Exception $e) { echo "获取机器列表失败:{$e->getMessage()}"; }
测试建议
先调用/users/me/端点验证身份是否成功,如果返回当前用户信息,说明签名逻辑正确。如果遇到401错误,优先检查:
- API密钥的三个部分是否拆分正确
- 签名密钥是否包含开头的
& - 请求头的OAuth参数编码是否符合规范
- MAAS服务器的API版本是否与请求端点匹配
内容的提问来源于stack exchange,提问作者Pete Wall
相关产品推荐
相关产品推荐

