使用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
相关产品推荐
相关产品推荐

