azure C#:通过MS Graph下载的Sharepoint文件返回未经授权的内容

yshpjwxd  于 2023-02-25  发布在  C#
关注(0)|答案(1)|浏览(156)

我正尝试使用Microsoft Graph承载令牌从Sharepoint下载文件。我继续收到401未授权响应。该应用程序将从Azure内运行,但在我确定该应用程序时,该应用程序当前在本地运行。代码如下所示,注解代替了可能的敏感信息:

using System.Net.Http.Headers;
using System.Text;

namespace SharePointFileDownload
{
    class Program
    {
        static async Task Main(string[] args)
        {
            try
            {
                string tenantId = /*My company's tenantId, from Azure*/;
                string accessToken = await GetGraphBearerToken();

                string tenant = /*the name of the tenant on sharepoint*/;
                string siteName = /*sharepoint site name*/;
                string library = /*sharepoint library name*/;
                string fileName = /*filename of the test document*/;

                string fileUrl = $"https://{tenant}.sharepoint.com/sites/{siteName}/{library}/{fileName}";

                using (var client = new HttpClient())
                {
                    client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);

                    string graphRequestUrl = $"https://graph.microsoft.com/v1.0/tenants/{tenantId}/drives/root:{fileUrl.Substring(fileUrl.IndexOf("/sites"))}:/content";

                    HttpResponseMessage response = await client.GetAsync(graphRequestUrl);

                    if (response.IsSuccessStatusCode)
                    {
                        byte[] fileContent = await response.Content.ReadAsByteArrayAsync();
                        File.WriteAllBytes("file.docx", fileContent);
                        Console.WriteLine("File downloaded successfully.");
                    }
                    else
                    {
                        Console.WriteLine("Failed to download file. Error: " + response.ReasonPhrase);
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex);
            }
        }

        public static async Task<string> GetGraphBearerToken()
        {
            try
            {
                string clientId = /*Azure ClientId for the registered application*/; 
                string clientSecret = /*Azure ClientSecret for the registered application*/;
                string tenantId = /*My company's tenantId, same as in the main method*/;

                var client = new HttpClient();
                client.DefaultRequestHeaders.Accept.Clear();
                client.DefaultRequestHeaders.Accept.Add(
                    new MediaTypeWithQualityHeaderValue("application/x-www-form-urlencoded"));

                var requestBody = new StringContent($"grant_type=client_credentials&client_id={clientId}&client_secret={clientSecret}&scope=https://graph.microsoft.com/.default", Encoding.UTF8, "application/x-www-form-urlencoded");

                var response = await client.PostAsync($"https://login.microsoftonline.com/{tenantId}/oauth2/v2.0/token", requestBody);

                if (response.IsSuccessStatusCode)
                {
                    var responseContent = await response.Content.ReadAsStringAsync();
                    // parse the responseContent to get the bearer token
                    Console.WriteLine("Bearer token: " + responseContent);
                    return responseContent;
                }
                else
                {
                    Console.WriteLine("Error fetching bearer token: " + response.StatusCode);
                    return String.Empty;
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex);
                return String.Empty;
            }
        }
    }
}

上面的代码是从各种教程中汇编出来的。它成功地获取了一个令牌并将其返回给main方法,但随后我得到了一个401未经授权的响应。
我向ClientId和ClientSecret所针对的Azure应用程序添加了许多权限,包括Microsoft_Graph/Files.ReadWrite.All和Microsoft_Graph/Sites. ReadWrite. All。
任何有Sharepoint和Graph经验的人告诉我哪里出了问题?或者代码是否可以以任何方式优化?非常感谢。
附言:下面是我得到的完整回复的一个例子,如果有帮助的话:

StatusCode: 401, ReasonPhrase: 'Unauthorized', Version: 1.1, Content: System.Net.Http.HttpConnectionResponseContent, Headers:
{
  Transfer-Encoding: chunked
  Strict-Transport-Security: max-age=31536000
  request-id: /*An ID, the same as client-request-id*/
  client-request-id: /*An ID, the same as request-id*/
  x-ms-ags-diagnostic: {"ServerInfo":{"DataCenter":"South Central US","Slice":"E","Ring":"5","ScaleUnit":"004","RoleInstance":/*Not sure if this is private, but here is a comment anyway*/}}
  WWW-Authenticate: Bearer realm="", authorization_uri="https://login.microsoftonline.com/common/oauth2/authorize", client_id=/*A ClientId that does not match the clientId variable I passed*/
  Date: Tue, 21 Feb 2023 16:55:47 GMT
  Content-Type: application/json
}
bfnvny8b

bfnvny8b1#

我同意*@TinyWang * 和*@user2250152 ,您需要在使用客户端凭据流程时添加*Application类型的API权限。
但是,当我添加
Application权限后运行您的代码时,仍然出现未经授权**错误。

我尝试在我的环境中重现相同的结果,结果如下:

我有一个名为**TestDocLib的SharePoint文档库**,其文件如下:

我注册了一个Azure AD应用程序,并添加了**Application类型的相同API权限**,如下所示:

当我运行与您相同的**c#代码时,我也获得了访问令牌,并出现如下Unauthorized**错误:

using System.Net.Http.Headers;
using System.Text;

namespace SharePointFileDownload
{
    class Program
    {
        static async Task Main(string[] args)
        {
            try
            {
                string tenantId = <tenantID>;
                string accessToken = await GetGraphBearerToken();

                string tenant = <tenantname>;
                string siteName =<sitename>;
                string library = <documentlibrary name>;
                string fileName = <filename>;

                string fileUrl = $"https://{tenant}.sharepoint.com/sites/{siteName}/{library}/{fileName}";

                using (var client = new HttpClient())
                {
                    client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);

                    string graphRequestUrl = $"https://graph.microsoft.com/v1.0/tenants/{tenantId}/drives/root:{fileUrl.Substring(fileUrl.IndexOf("/sites"))}:/content";

                    HttpResponseMessage response = await client.GetAsync(graphRequestUrl);

                    if (response.IsSuccessStatusCode)
                    {
                        byte[] fileContent = await response.Content.ReadAsByteArrayAsync();
                        File.WriteAllBytes("file.docx", fileContent);
                        Console.WriteLine("File downloaded successfully.");
                    }
                    else
                    {
                        Console.WriteLine("Failed to download file. Error: " + response.ReasonPhrase);
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex);
            }
        }

        public static async Task<string> GetGraphBearerToken()
        {
            try
            {
                string clientId = <appID>; 
                string clientSecret = <secret>;
                string tenantId = <tenantID>;

                var client = new HttpClient();
                client.DefaultRequestHeaders.Accept.Clear();
                client.DefaultRequestHeaders.Accept.Add(
                    new MediaTypeWithQualityHeaderValue("application/x-www-form-urlencoded"));

                var requestBody = new StringContent($"grant_type=client_credentials&client_id={clientId}&client_secret={clientSecret}&scope=https://graph.microsoft.com/.default", Encoding.UTF8, "application/x-www-form-urlencoded");

                var response = await client.PostAsync($"https://login.microsoftonline.com/{tenantId}/oauth2/v2.0/token", requestBody);

                if (response.IsSuccessStatusCode)
                {
                    var responseContent = await response.Content.ReadAsStringAsync();
                    // parse the responseContent to get the bearer token
                    Console.WriteLine("Bearer token: " + responseContent);
                    return responseContent;
                }
                else
                {
                    Console.WriteLine("Error fetching bearer token: " + response.StatusCode);
                    return String.Empty;
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex);
                return String.Empty;
            }
        }
    }
}
    • 答复:**

当我解码上述访问令牌时,它具有两个权限的**roles声明,确认令牌是valid**,如下所示:

要知道问题的确切位置,请尝试在Postman中运行相同的查询,并检查是否得到相同的结果。
在我的例子中,我通过包含上述访问令牌在Postman中运行了不同的图形查询,并在响应中获得了文件下载链接,如下所示:

GET https://graph.microsoft.com/v1.0/sites/<siteID>/drives/<doclib driveID>/root/children/<filename>
    • 答复:**

当我复制上述下载链接并在浏览器中运行时,文件成功下载,如下所示:

为了确认这一点,我检查了相同的文件资源管理器,并发现**sri12.docx**文件在下载文件夹中,如下所示:

要在C#中运行相同的查询,您可以使用以下代码:

using Microsoft.Graph;
using Azure.Identity;

string tenantId = "<tenantID>";
string clientId = "<appID>";
string clientSecret = "<secret>";

// using Azure.Identity;
var options = new TokenCredentialOptions
{
    AuthorityHost = AzureAuthorityHosts.AzurePublicCloud
};

var scopes = new[] { "https://graph.microsoft.com/.default" };
var clientSecretCredential = new ClientSecretCredential(
    tenantId, clientId, clientSecret, options);

var graphClient = new GraphServiceClient(clientSecretCredential, scopes);

var driveItem = await graphClient.Sites["<siteID>"].Drives["<driveID>"].Root.Children["filename.docx"]
    .Request()
    .GetAsync();

相关问题