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

如何在Flask应用中通过服务账号密钥管控Data Studio报表访问

Looker Studio(原Data Studio)服务账号密钥管控访问方案

核心逻辑:关闭报表原生公网共享,所有报表访问请求先经过Flask后端做密钥校验,校验通过后才允许访问报表内容,服务账号仅分配最小必要权限,避免未授权访问。


前置GCP侧配置

  • 新建专用服务账号,不要复用已有高权限账号,给该账号分配两类权限:
    • BigQuery侧:绑定报表依赖数据集的BigQuery Data Viewer、BigQuery Job User角色,保证账号能正常查询底表数据渲染报表
    • Looker Studio侧:将目标报表共享给该服务账号,仅授予「查看者」权限,不要给编辑/管理权限
  • 为该服务账号生成JSON格式密钥文件,存到Flask项目根目录,将文件名加入.gitignore,避免密钥随代码泄露
  • 调整报表共享设置:关闭公开链接共享,将访问范围设置为「仅被邀请的用户可访问」,从根源切断原生链接的未授权访问路径

后端逻辑改造

先安装依赖包:
pip install flask google-auth requests

替换原有app.py代码,新增密钥校验装饰器、权限拦截逻辑、错误返回逻辑:

from flask import Flask, render_template, redirect, request, abort
from google.oauth2 import service_account
import requests
from functools import wraps

app = Flask(__name__)
# 配置项,替换为自己的实际参数
VALID_ACCESS_KEYS = {"key_abc123", "key_def456"}  # 可配置多个有效密钥,分配给不同用户
SA_KEY_PATH = "./service-account.json"  # 服务账号JSON密钥的本地路径
REPORT_EMBED_URL = "你的Looker Studio报表嵌入地址"
REPORT_SHARE_URL = "你的Looker Studio报表共享链接"

def access_required(f):
    """权限校验装饰器,所有需要访问报表的路由都加这个装饰器"""
    @wraps(f)
    def decorated(*args, **kwargs):
        # 优先从请求头取密钥,兼容URL参数传参,生产环境建议只用请求头传密钥,避免密钥留存在浏览器历史
        req_key = request.headers.get("X-Access-Key") or request.args.get("key")
        if not req_key or req_key not in VALID_ACCESS_KEYS:
            abort(403, "访问密钥无效,请联系管理员获取有效权限")
        return f(*args, **kwargs)
    return decorated

@app.route('/')
@access_required
def index():
    return render_template('index.html')

@app.route('/report')
@access_required
def report_redirect():
    # 校验通过后才跳转,可扩展为生成短时效签名链接,避免链接被转发后长期可用
    return redirect(REPORT_SHARE_URL)

@app.route('/embed-content')
@access_required
def embed_proxy():
    """代理嵌入报表内容,不直接暴露原生报表地址"""
    creds = service_account.Credentials.from_service_account_file(
        SA_KEY_PATH,
        scopes=["https://www.googleapis.com/auth/lookerstudio.readonly"]
    )
    creds.refresh(requests.Request())
    return redirect(f"{REPORT_EMBED_URL}?access_token={creds.token}")

@app.errorhandler(403)
def handle_403(e):
    return render_template('403.html', err_info=e.description), 403

if __name__ == '__main__':
    app.run(host='127.0.0.1', port=8080, debug=True)

前端模板改造

  1. 修改templates/index.html,替换原有硬编码的iframe地址,适配密钥透传逻辑:
<!DOCTYPE html>
<html>
<body>
<h1>Datastudio Test</h1>
<p>Datastudio Report</p>
<button type="button" onclick="window.location.href=`/report?key=${new URLSearchParams(window.location.search).get('key')}`">打开独立报表页</button>
<iframe width="600" height="800" src="/embed-content?key={{ request.args.get('key') }}" frameborder="0" style="border:0" allowfullscreen></iframe>
</body>
</html>
  1. 新增templates/403.html作为无权限提示页:
<!DOCTYPE html>
<html>
<body>
<h1>访问被拒绝</h1>
<p>*{{ err_info }}*</p>
</body>
</html>

生产环境加固建议

  • 不要把有效密钥硬编码在代码文件中,生产环境建议将密钥存在环境变量或专用密钥管理服务中,定期轮换
  • 服务账号生成的访问令牌默认有效期为1小时,不要手动延长令牌时效,降低令牌泄露后的风险
  • 安全要求较高的场景可以去掉/report的直接跳转逻辑,所有报表内容通过/embed-content做服务端反向代理,用户端完全接触不到报表原生地址
  • 增加访问日志,记录每个密钥对应的访问IP、访问时间,方便排查异常访问行为

注意:严格遵循最小权限原则,不要给服务账号分配报表所有者、BigQuery编辑等高权限,避免密钥泄露后数据被篡改。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 19:21:13