我尝试使用 Microsoft Graph 不记名 token 从 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;
}
}
}
}
上面的代码是根据各种教程组装而成的。它成功获取 token 并将其返回到 main 方法,但随后我收到 401 Unauthorized 响应。
我已向 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
}
最佳答案
I agree with both @TinyWang and @user2250152, you need to add API permissions of
Application
type while using client credentials flow.
但是,当我在添加应用程序
权限后运行您的代码时,它仍然给出未经授权错误。
我尝试在我的环境中重现相同的结果并得到以下结果:
我有一个名为 TestDocLib
的 SharePoint 文档库,其中包含如下文件:
我注册了一个 Azure AD 应用程序,并添加了与应用程序
类型相同的 API 权限,如下所示:
当我运行与您相同的c#代码时,我也获得了访问 token ,并出现未经授权
错误,如下所示:
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;
}
}
}
}
回应:
当我解码上述访问 token 时,它具有 roles
声明,并具有确认 token 有效的两种权限,如下所示:
要了解问题到底出在哪里,请尝试在 Postman 中运行相同的查询,并检查是否得到相同的结果。
就我而言,我通过包含上述访问 token 在 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();
关于C#:通过 MS Graph 下载 Sharepoint 文件返回未经授权,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/75523670/