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

使用google.cloud.storage无法获取Firebase Storage文件链接的问题

问题描述

此前用无服务账号密钥的Pyrebase客户端,配合Firebase Storage规则能正常上传和访问文件。现在改用google.cloud.storage.Client()结合用户凭证时出现异常,具体情况如下:

相关代码

获取头像的函数

def readAva(dictCred):   #creds dictionary    
    cred = Credentials(dictCred["user"]["stsTokenManager"]['accessToken'])
    stor = storage.Client(project='fbtest-XXX', credentials=cred)
    print("CREDs OK")

    fName = dictCred["user"]["uid"] + ".png"
    url = stor.get_bucket("fbtest-XXX.appspot.com").blob(f"test-users/{fName}").public_url    
    print("URL=" + url)
    
    return url

Flask调用代码

def userPage(userid):    
    avaURL = ""

    if request.method == "GET":
        #if userid != session["userid"]: pageNotFound(404) 
        #else:
            try:
                avaURL = storage.readAva(session["dictCred"])             
            except:
                print("Can't read an avatar!")    

    return render_template("home.html", avaURL=avaURL)

异常表现

运行时会打印CREDs OK,但触发异常分支输出Can't read an avatar!,浏览器控制台无错误,图片src为空。直接写死blob路径也无效。

其他信息

  • 该凭证可正常用于firestore.Client()
  • Firebase Storage规则配置:
service firebase.storage {
  match /b/{bucket}/o {    
    match /test-users/{filename} {
      allow read: if request.auth != null; 
    }
  }
}
  • 模拟GET请求有效,文件确实存在于test-users目录中

解决方案

核心问题

直接用用户的accessToken创建Credentials对象,无法通过Firebase Storage的安全规则校验。Firebase Storage的规则依赖完整的Firebase Auth上下文,普通Google Cloud Storage客户端不会自动处理Firebase的auth验证逻辑——哪怕这个凭证能正常用在Firestore上也没用。另外,public_url是无凭证的公开URL,完全不符合你设置的request.auth != null规则,必然会被拦截。

修复方案

方案1:用用户ID Token生成签名URL

改用用户的ID Token初始化客户端,生成带权限的临时签名URL:

def readAva(dictCred):
    # 改用用户的ID Token而非accessToken
    id_token = dictCred["user"]["stsTokenManager"]['idToken']
    bucket_name = "fbtest-XXX.appspot.com"
    uid = dictCred["user"]["uid"]
    blob_path = f"test-users/{uid}.png"

    cred = Credentials(id_token)
    stor = storage.Client(project='fbtest-XXX', credentials=cred)
    blob = stor.get_bucket(bucket_name).blob(blob_path)

    # 生成1小时有效期的签名URL,携带用户权限
    signed_url = blob.generate_signed_url(
        expiration=3600,
        method='GET'
    )
    return signed_url

方案2:通过REST API携带ID Token请求

如果不想用Storage客户端,可直接调用Firebase Storage的REST API,带上用户ID Token:

import requests

def readAva(dictCred):
    id_token = dictCred["user"]["stsTokenManager"]['idToken']
    bucket_name = "fbtest-XXX.appspot.com"
    uid = dictCred["user"]["uid"]
    # 转义路径中的斜杠
    encoded_blob_path = f"test-users/{uid}.png".replace('/', '%2F')
    # 构造带alt=media的资源URL
    url = f"https://firebasestorage.googleapis.com/v0/b/{bucket_name}/o/{encoded_blob_path}?alt=media"
    
    headers = {"Authorization": f"Bearer {id_token}"}
    response = requests.get(url, headers=headers)
    
    if response.status_code == 200:
        # 如果要返回URL给前端,需要前端请求时也携带相同的Authorization头;若返回图片二进制则直接return response.content
        return url
    else:
        raise Exception("Failed to fetch avatar")

关键注意点

  • Firestore与Storage的权限校验逻辑不同:Firestore可直接使用用户的accessToken,但Storage规则校验依赖完整的Firebase Auth凭证(ID Token)。
  • 测试时可临时将规则改为allow read: true;验证文件是否存在,但生产环境必须改回原规则。

内容的提问来源于stack exchange,提问作者Mikhail Nikolaev

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 09:33:45