我正在为存储 API 创建一个 API 请求(GET 存储桶),其中一个必需的参数是"授权"标头。
请注意,我正在使用服务帐户访问 API。
我按照文档 https://developers.google.com/accounts/docs/OAuth2ServiceAccount 获取"授权"标头的访问令牌,以便我可以向他们的 REST API 发送授权请求。问题是我总是收到"invalid_grant"错误。
使用此代码进行检查:
<?php
error_reporting(E_ERROR);
const CLIENT_ID = 'XXXXXXXXXXXX.apps.googleusercontent.com';
const SERVICE_ACCOUNT = 'XXXXXXXXXXXX@developer.gserviceaccount.com';
const KEY_FILE = 'XXX.p12';
function get_oauth_access_token()
{
$header[alg] = 'RS256';
$header[typ] = 'JWT';
$header = urlencode(base64_encode(utf8_encode(json_encode($header))));
$assertion_time = time();
$claim[iss] = CLIENT_ID; //also tried SERVICE_ACCOUNT here, no improvement
$claim[scope] = 'https://www.googleapis.com/auth/devstorage.read_only';
$claim[aud] = 'https://accounts.google.com/o/oauth2/token';
$claim[exp] = $assertion_time + 3600;
$claim[iat] = $assertion_time;
$claim = urlencode(base64_encode(utf8_encode(json_encode($claim))));
$data = $header . '.' . $claim;
$p12 = file_get_contents(KEY_FILE);
$cert = array();
openssl_pkcs12_read($p12, $cert, 'notasecret');
$priv_key_id = openssl_get_privatekey($cert[pkey]);
openssl_sign($data, $signature, $priv_key_id, 'sha256');
$signature = urlencode(base64_encode($signature));
$assertion = $data . '.' . $signature;
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'https://accounts.google.com/o/oauth2/token');
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, array('grant_type'=>'assertion',
'assertion_type'=>'http://oauth.net/grant_type/jwt/1.0/bearer',
'assertion'=>$assertion));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$result = curl_exec($ch);
$error = curl_error($ch);
curl_close($ch);
var_dump($result);
var_dump($error);
}
get_oauth_access_token();
此代码中是否存在导致"invalid_grant"错误的错误?
您的问题可能是服务器的时间。
我在谷歌分析中遇到了一个问题:我的所有代码都是正确的,但我的服务器时间与谷歌的时间相比是未来的几秒钟。您可以将服务器的时间延迟几分钟来进行测试。
如果它有效,您可以使用NTP来保持服务器时钟正确。
下面是一个简单的PHP程序,说明了使用Google Cloud Storage RESTful HTTP接口的服务帐户。我已经使用服务帐户测试了此代码,它似乎工作正常。如果您有任何其他问题,请告诉我。
<?php
require_once 'apiClient.php';
// Define constants.
const CLIENT_ID = 'YOUR_CLIENT_ID_GOES_HERE';
const SERVICE_ACCOUNT_NAME = 'YOUR_SERVICE_ACCOUNT_NAME_GOES_HERE';
const KEY_FILE = 'key.p12';
const BUCKET = 'marc-us';
// Obtain OAuth 2.0 access token for service account.
$client = new apiClient();
$key = file_get_contents(KEY_FILE);
$client->setAssertionCredentials(new apiAssertionCredentials(
SERVICE_ACCOUNT_NAME,
array('https://www.googleapis.com/auth/devstorage.full_control'),
$key)
);
$client::$auth->refreshTokenWithAssertion();
$json = $client->getAccessToken();
$accessToken = json_decode($json)->access_token;
// Add access token to Authorization header of HTTP request.
$ch = curl_init('http://commondatastorage.googleapis.com/' . BUCKET);
curl_setopt($ch, CURLOPT_HTTPHEADER,
array('Authorization: OAuth ' . $accessToken));
$resp = curl_exec($ch);
curl_close($ch);
// Display results.
print '<h2>Response:</h2><pre>' . $resp . '</pre>';
?>
我上一个回复中的代码对我来说效果很好,所以我怀疑你正面临环境问题。无论如何,我咨询了Google PHP客户端库的所有者,他提供了一种更好的方法来刷新访问令牌,而无需调用内部refreshTokenWithAssertion()方法。他提出了这种技术:
$req = new apiHttpRequest(YOUR_URL);
$val = $client->getIo()->authenticatedRequest($req);
对 authenticatedRequest() 的调用负责根据需要刷新访问令牌(如果设置了断言凭据),则使用断言凭据。我修改了上面的代码以使用这种方法,它对我来说效果很好。请注意,旧版本和新版本都对我有用,因此没有功能差异,但我认为新版本更好,因为它避免了内部调用,使用 Google PHP 客户端库而不是 curl 来执行请求并产生更短、更简单的代码。
<?php
require_once 'apiClient.php';
// Define constants.
const SERVICE_ACCOUNT_NAME = 'YOUR_SERVICE_ACCOUNT_NAME';
const KEY_FILE = 'key.p12';
const BUCKET = 'marc-us';
// Obtain service account credentials assertion.
$client = new apiClient();
$key = file_get_contents(KEY_FILE);
$client->setAssertionCredentials(new apiAssertionCredentials(
SERVICE_ACCOUNT_NAME,
array('https://www.googleapis.com/auth/devstorage.full_control'),
$key)
);
// Create HTTP request, authorize and execute.
$req = new apiHttpRequest('http://commondatastorage.googleapis.com/' . BUCKET);
$resp = $client::getIo()->authenticatedRequest($req);
// Display results.
print '<h2>Response:</h2>' . $resp->getResponseBody();
?>
我遇到了同样的错误,但使用 Java。我的问题是,exp 和 iat 的时间(以秒为单位)在 GMT-5 中,而 API 谷歌身份验证在 GMT 中。然后我将时间更改为 GMT 值,一切都很好。