You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何通过Microsoft Graph Python SDK,在仅拥有Sites.Selected应用权限的情况下获取指定SharePoint站点的ID及元数据

如何通过Microsoft Graph Python SDK,在仅拥有Sites.Selected应用权限的情况下获取指定SharePoint站点的ID及元数据

嘿,我来帮你搞定这个问题!首先得明确:Sites.Selected这个应用权限是需要你先给目标站点单独授权的,这是很多人踩坑的点——光在Azure AD里配置了权限还不够,必须把应用的访问权限绑定到你要操作的那个SharePoint站点上,不然所有请求都会返回空结果或者403错误。

第一步:先确认你的应用已获得目标站点的授权

在调用任何获取站点的接口前,你需要先通过Graph API给应用授予目标站点的访问权限。举个例子,你可以发送一个POST请求到:

https://graph.microsoft.com/v1.0/sites/mycompanyname.sharepoint.com:/sites/mysitename/permissions

请求体格式如下(替换成你的应用信息):

{
  "roles": ["Read"],
  "grantedToIdentities": [
    {
      "application": {
        "id": "你的应用Client ID",
        "displayName": "你的应用名称"
      }
    }
  ]
}

这一步完成后,你的应用才真正拥有了该站点的访问权限。

第二步:修正你的代码,用正确方式获取站点信息

你之前尝试的Example 2思路是对的,但可能因为权限没配置好才失败。下面是修正后的完整代码示例,专门针对你的需求:

首先,保留你的单例Graph客户端(可以继续用)

import os
import jwt
from typing import Optional
from azure.identity.aio import ClientSecretCredential
from msgraph import GraphServiceClient
from msgraph.generated.models.o_data_errors.o_data_error import ODataError
from kiota_abstractions.base_request_configuration import RequestConfiguration
# 导入你的配置和日志模块
from src.config.loader import CONFIG
from src.utils.logger import get_logger

logger = get_logger(CONFIG.logging.name, CONFIG.logging.level)

class GraphClientSingleton():
    _client = None
    _credentials = None

    @classmethod
    def get_client(cls):
        if cls._client is None:
            cls._credentials = ClientSecretCredential(
                tenant_id = CONFIG.msgraph.tenant_id_sharepoint,
                client_id = CONFIG.msgraph.client_id_sharepoint,
                client_secret = CONFIG.msgraph.client_secret_sharepoint,
            )
            cls._scopes = ['https://graph.microsoft.com/.default']

            cls._client = GraphServiceClient(credentials=cls._credentials, scopes=cls._scopes)
        return cls._client

    @classmethod
    async def decode_token(cls):
        token = await cls.get_access_token()
        decoded_token = jwt.decode(token, options={"verify_signature": False})
        return decoded_token

    @classmethod
    async def get_access_token(cls):
        if cls._credentials is None:
            cls.get_client()
        
        token = await cls._credentials.get_token(*cls._scopes)
        return token.token

然后,修改SharePointDAO类,添加获取指定站点的方法

class SharePointDAO:
    def __init__(self):
        self.client = GraphClientSingleton.get_client()

    async def get_target_site_metadata(self, site_full_url: str) -> Optional[dict]:
        """通过完整站点URL获取站点ID和元数据"""
        # 解析站点URL,转换成Graph需要的标识符格式
        from urllib.parse import urlparse
        parsed_url = urlparse(site_full_url)
        tenant_domain = parsed_url.netloc  # 得到mycompanyname.sharepoint.com
        site_path = parsed_url.path.lstrip('/')  # 得到sites/mysitename
        site_identifier = f"{tenant_domain}:/{site_path}"

        try:
            # 构建站点请求
            site_request = self.client.sites.with_url(
                f"https://graph.microsoft.com/v1.0/sites/{site_identifier}"
            )
            # 指定要获取的字段,避免返回冗余数据
            request_config = RequestConfiguration(
                query_parameters = {
                    "select": ["id", "title", "webUrl", "siteCollection"]
                }
            )
            # 发送请求
            site = await site_request.get(request_configuration=request_config)
            
            if site:
                return {
                    "site_id": site.id,
                    "site_title": site.title,
                    "site_web_url": site.web_url,
                    "site_collection_id": site.site_collection.id if site.site_collection else None
                }
            return None
        except ODataError as e:
            logger.error(f"获取站点数据失败:{e.error.message}")
            logger.error(f"错误代码:{e.error.code}")
            return None

最后,测试代码

if __name__ == "__main__":
    async def test_site_access():
        sp_dao = SharePointDAO()
        target_site = "https://mycompanyname.sharepoint.com/sites/mysitename"
        site_metadata = await sp_dao.get_target_site_metadata(target_site)
        
        if site_metadata:
            logger.info("成功获取站点信息:")
            logger.info(f"站点ID: {site_metadata['site_id']}")
            logger.info(f"站点标题: {site_metadata['site_title']}")
        else:
            logger.warning("无法获取站点信息,请检查权限配置是否正确")

    import asyncio
    asyncio.run(test_site_access())

为什么你之前的尝试失败了?

  • Example 1/3:直接调用sites.get()或者带filter的查询,在Sites.Selected权限下,只会返回你已经授权的站点。如果没给目标站点授权,自然返回空列表。
  • Example 4get_all_sites接口需要Sites.Read.All全局权限,你的Sites.Selected权限不足以调用,所以返回403。

关键提醒

  • 一定要确保应用已被授予目标站点的Sites.Selected权限,这是前提!
  • 始终使用**站点标识符格式(租户域名:/站点路径)**来定位目标站点,这是Graph API中针对已知URL站点的标准查询方式。

备注:内容来源于stack exchange,提问作者Luca

火山引擎 最新活动