调用API启停Google Compute Engine实例遇401认证错误求助
解决Google Compute API 401 Invalid Credentials错误的正确姿势
看起来你在认证方式上踩了个常见的坑——API Key不能用来做Bearer令牌,而且Compute Engine的实例启停这类操作,API Key本身就没有权限,因为它只适用于不需要用户身份的公开只读API。咱们一步步把问题解决:
为什么之前的方法行不通?
Authorization: Bearer <api_key>是完全错误的用法:Bearer令牌必须是OAuth 2.0的访问令牌,不是API Key。API Key只是用来标识你的项目,没有身份授权能力。- 即使在URL里加
key=<api_key>,Compute Engine的写操作(启停实例)也不支持这种认证方式,因为这类操作需要验证请求者是否有对应的资源权限。
正确的解决方案:使用服务账号认证(后台服务最佳实践)
对于PHP后台这类无人值守的服务,最稳妥的方式是用服务账号来完成认证,步骤如下:
1. 创建服务账号并分配权限
- 到Google Cloud控制台的「IAM与管理」→「服务账号」页面,创建一个新的服务账号。
- 给这个服务账号分配合适的权限:比如
Compute Instance Admin (v1)(如果需要全实例管理权限),或者更精细的Compute Instances Start和Compute Instances Stop权限(遵循最小权限原则)。 - 下载服务账号的JSON密钥文件,保存到你的PHP项目目录(注意不要提交到代码仓库)。
2. 使用Google官方PHP客户端库(推荐)
直接用Google提供的google/cloud-compute库,它会自动处理认证流程,比自己写curl省心太多:
首先安装依赖:
composer require google/cloud-compute
然后写启停实例的代码:
require __DIR__ . '/vendor/autoload.php'; use Google\Cloud\Compute\V1\InstancesClient; use Google\Cloud\Compute\V1\StartInstanceRequest; use Google\Cloud\Compute\V1\StopInstanceRequest; // 初始化客户端,指定服务账号密钥路径 $config = [ 'keyFilePath' => '/path/to/your/service-account-key.json' ]; $instancesClient = new InstancesClient($config); // 启动实例的示例 try { $startRequest = (new StartInstanceRequest()) ->setProject('your-project-id') ->setZone('your-instance-zone') // 比如us-central1-a ->setInstance('your-instance-name'); $operation = $instancesClient->start($startRequest); // 等待操作完成(可选) $operation->pollUntilComplete(); echo "实例启动成功!"; } catch (Exception $e) { echo "启动失败:" . $e->getMessage(); } finally { $instancesClient->close(); } // 停止实例的示例 try { $stopRequest = (new StopInstanceRequest()) ->setProject('your-project-id') ->setZone('your-instance-zone') ->setInstance('your-instance-name'); $operation = $instancesClient->stop($stopRequest); $operation->pollUntilComplete(); echo "实例停止成功!"; } catch (Exception $e) { echo "停止失败:" . $e->getMessage(); } finally { $instancesClient->close(); }
3. 如果一定要手动用Curl(不推荐)
如果你不想用客户端库,需要先通过服务账号密钥生成OAuth 2.0访问令牌,再用这个令牌作为Bearer令牌请求API:
// 第一步:生成JWT并获取访问令牌 $keyFilePath = '/path/to/your/service-account-key.json'; $keyData = json_decode(file_get_contents($keyFilePath), true); // 构造JWT payload $iat = time(); $exp = $iat + 3600; // 令牌有效期1小时 $payload = [ 'iss' => $keyData['client_email'], 'sub' => $keyData['client_email'], 'aud' => 'https://www.googleapis.com/oauth2/v4/token', 'iat' => $iat, 'exp' => $exp, 'scope' => 'https://www.googleapis.com/auth/compute' ]; // 生成JWT签名 $privateKey = openssl_pkey_get_private($keyData['private_key']); openssl_sign(json_encode($payload), $signature, $privateKey, 'SHA256'); $jwt = base64url_encode(json_encode(['alg' => 'RS256', 'typ' => 'JWT'])) . '.' . base64url_encode(json_encode($payload)) . '.' . base64url_encode($signature); // 请求访问令牌 $tokenCurl = curl_init(); curl_setopt_array($tokenCurl, [ CURLOPT_URL => 'https://www.googleapis.com/oauth2/v4/token', CURLOPT_POST => true, CURLOPT_HTTPHEADER => ['Content-Type: application/x-www-form-urlencoded'], CURLOPT_POSTFIELDS => http_build_query([ 'grant_type' => 'urn:ietf:params:oauth:grant-type:jwt-bearer', 'assertion' => $jwt ]), CURLOPT_RETURNTRANSFER => true ]); $tokenResponse = curl_exec($tokenCurl); curl_close($tokenCurl); $tokenData = json_decode($tokenResponse, true); $accessToken = $tokenData['access_token']; // 第二步:用访问令牌调用启停实例API // 启动实例 $startCurl = curl_init(); curl_setopt_array($startCurl, [ CURLOPT_URL => 'https://compute.googleapis.com/compute/v1/projects/your-project-id/zones/your-zone/instances/your-instance-name/start', CURLOPT_CUSTOMREQUEST => 'POST', CURLOPT_HTTPHEADER => [ 'Authorization: Bearer ' . $accessToken, 'Content-Type: application/json' ], CURLOPT_RETURNTRANSFER => true ]); $startResponse = curl_exec($startCurl); curl_close($startCurl); // 停止实例类似,把URL换成对应的/stop路径
注意:base64url_encode需要自己实现,因为PHP的base64_encode是标准base64,而JWT需要URL安全的版本:
function base64url_encode($data) { return rtrim(strtr(base64_encode($data), '+/', '-_'), '='); }
最后检查几个关键点
- 确保服务账号的权限已经正确分配,并且权限已经生效(有时候会有几分钟的延迟)。
- 确认你的PHP环境能访问Google的API,没有被防火墙或代理拦截。
- 密钥文件的路径要正确,并且文件权限设置合理(不要让其他用户可读)。
内容的提问来源于stack exchange,提问作者mockdev
相关产品推荐
相关产品推荐

