如何通过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 4:
get_all_sites接口需要Sites.Read.All全局权限,你的Sites.Selected权限不足以调用,所以返回403。
关键提醒
- 一定要确保应用已被授予目标站点的Sites.Selected权限,这是前提!
- 始终使用**站点标识符格式(租户域名:/站点路径)**来定位目标站点,这是Graph API中针对已知URL站点的标准查询方式。
备注:内容来源于stack exchange,提问作者Luca




