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

如何实现同项目Cloud Function调用App Engine的身份认证

好问题!既然你的Cloud Function和App Engine同属一个GCP项目,用服务账号的ID Token来做身份认证是完全可行的——这是最安全且省心的方案,不需要手动管理密钥,GCP会自动处理凭据的轮换。下面我一步步给你讲具体操作:

解决方案:使用服务账号ID Token实现跨服务认证

核心思路

Cloud Function利用自身运行环境默认挂载的服务账号凭据,生成一个针对App Engine目标资源的ID Token,将这个Token放在请求的Authorization头中;App Engine端验证Token的有效性(包括签名、受众、签发者等),验证通过后再处理业务请求。

步骤1:修改Cloud Function代码,生成并携带ID Token

首先,你需要在Cloud Function的依赖中添加google-auth库(直接写在requirements.txt里即可,部署时会自动安装),然后调整请求逻辑:

import json
import urllib.request
from google.oauth2 import id_token
from google.auth.transport import requests

def your_cloud_function_handler(event, context):
    # 目标App Engine资源的完整URL
    target_url = "https://project-name.appspot.com/urlidentifier"
    
    # 生成ID Token:受众必须设置为目标资源的URL
    try:
        id_token_str = id_token.fetch_id_token(requests.Request(), target_url)
    except Exception as e:
        print(f"生成ID Token失败: {str(e)}")
        return {"status": "error", "message": str(e)}, 500
    
    # 准备JSON格式的请求数据
    data_obj = {'sample': 'data'}
    data_json = json.dumps(data_obj).encode('utf-8')
    
    # 构建请求,添加Authorization头
    req = urllib.request.Request(
        target_url,
        data=data_json,
        headers={
            'Content-Type': 'application/json',
            'Authorization': f'Bearer {id_token_str}'
        }
    )
    
    try:
        resp = urllib.request.urlopen(req)
        response_data = resp.read().decode('utf-8')
        return {"status": "success", "response": response_data}, 200
    except urllib.error.HTTPError as e:
        return {"status": "error", "code": e.code, "message": e.read().decode('utf-8')}, e.code

关键细节:

  • id_token.fetch_id_token会自动读取Cloud Function运行环境中的默认服务账号凭据,完全不需要手动下载密钥文件。
  • 受众(Audience)必须严格设置为目标App Engine资源的完整URL,这是Token验证的核心依据。
  • 把原来的urlencode逻辑改成直接发送JSON数据,符合常规的API请求格式,也方便App Engine端解析。

步骤2:修改App Engine应用,验证ID Token

你的App Engine原本用Firebase用户认证,现在需要添加服务账号ID Token的验证逻辑。以下是Python 2.7的示例代码(假设你用的是Webapp2框架):

import webapp2
import json
from google.auth import jwt
from google.auth.transport import requests

class UrlIdentifierHandler(webapp2.RequestHandler):
    def post(self):
        # 提取Authorization头中的Token
        auth_header = self.request.headers.get('Authorization')
        if not auth_header or not auth_header.startswith('Bearer '):
            self.response.status = 401
            self.response.write(json.dumps({"error": "缺少有效认证信息"}))
            return
        
        id_token_str = auth_header.split(' ')[1]
        
        # 验证ID Token的有效性
        try:
            claims = jwt.verify(
                id_token_str,
                requests.Request(),
                # 受众必须和Cloud Function中设置的完全一致
                audience="https://project-name.appspot.com/urlidentifier",
                # 签发者固定为Google账号服务域名
                issuer="https://accounts.google.com"
            )
            
            # 可选:额外限制仅本项目服务账号可访问
            if not claims['email'].endswith('@project-name.iam.gserviceaccount.com'):
                raise ValueError("非本项目服务账号发起的请求")
                
        except Exception as e:
            self.response.status = 403
            self.response.write(json.dumps({"error": f"认证失败: {str(e)}"}))
            return
        
        # 验证通过,处理业务逻辑
        request_data = json.loads(self.request.body)
        self.response.write(json.dumps({"status": "success", "received_data": request_data}))

app = webapp2.WSGIApplication([
    ('/urlidentifier', UrlIdentifierHandler),
], debug=True)

关键细节:

  • 使用google.auth.jwt.verify可以自动验证Token的签名、过期时间、受众等核心信息,无需手动处理复杂的加密逻辑。
  • 可选的邮箱后缀检查可以进一步缩小访问范围,确保只有本项目内的服务账号能调用这个接口。

权限配置(无需额外操作)

因为Cloud Function和App Engine同属一个项目,Cloud Function的默认服务账号(PROJECT_ID@appspot.gserviceaccount.com)已经拥有生成ID Token的权限,不需要额外添加IAM角色。如果后续遇到权限问题,可以检查是否给该服务账号添加了Service Account Token Creator角色(同项目下默认已包含)。


内容的提问来源于stack exchange,提问作者Jack tileman

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.09 09:32:32