【问题标题】:How to access Sharepoint files using app-level authentication / access token?如何使用应用级身份验证/访问令牌访问 Sharepoint 文件?
【发布时间】:2021-08-18 20:51:36
【问题描述】:

我正在尝试无需用户登录即可访问 Sharepoint 文件。

我可以通过以下任一方式获取访问令牌 方法一:

var client = new RestClient("https://login.microsoftonline.com/app's-tenant-id-here/oauth2/token");
client.Timeout = -1;
var request = new RestRequest(Method.POST);
request.AddHeader("Content-Type", "application/x-www-form-urlencoded");
request.AddHeader("Cookie", "fpc=AjMRWuGtzbFJgrxV0V1kMCkUHKO3AQAAAEqqRtgOAAAA");
request.AddParameter("resource", "https://graph.microsoft.com");
request.AddParameter("grant_type", "client_credentials");
request.AddParameter("client_id", "client-id-here");
request.AddParameter("client_secret", ".client-secret-here");
IRestResponse response = client.Execute(request);

或方法 2 - (这给出了以下错误:'AppForSharePointOnlineWebToolkit.TokenHelper' 的类型初始化程序引发了异常。)

string siteUrl = "https://the-site-I-am-trying-to-access.sharepoint.com/sites/xxx/";
string realm = TokenHelper.GetRealmFromTargetUrl(new Uri(siteUrl));
string accessToken2 = TokenHelper.GetAppOnlyAccessToken(TokenHelper.SharePointPrincipal, new Uri(siteUrl).Authority, realm).AccessToken;
using (ClientContext cc = TokenHelper.GetClientContextWithAccessToken(siteUrl, accessToken2))
{
       cc.Load(cc.Web, p => p.Title);
       cc.ExecuteQuery();
       Console.WriteLine(cc.Web.Title);
}

甚至方法3

HttpWebRequest endpointRequest = (HttpWebRequest)WebRequest.Create("https://the-site-I-am-trying-to-access.sharepoint.com/sites/xxx/_api/web/getfilebyserverrelativeurl('~/Shared%20Documents/picture.png')");
endpointRequest.Method = "GET";
endpointRequest.Accept = "application/json;odata=verbose";
endpointRequest.Headers.Add("Authorization", "Bearer " + accessToken);
HttpWebResponse endpointResponse = (HttpWebResponse)endpointRequest.GetResponse();

没有一个成功访问 Sharepoint。

所以我的问题是,我做错了什么还是有其他方法可以做到这一点?

【问题讨论】:

    标签: c# azure api sharepoint


    【解决方案1】:

    好吧,首先我建议尽可能使用 Graph API。至少它是查询数据的首选方式,它使事情变得更容易。

    要通过 Graph API 访问 Microsoft 365 世界中的数据,需要在 azure portal > Azure Active Directory > 应用注册中创建新的应用注册。

    查看此链接了解更多信息:MS Docs App Registration

    创建新应用后,配置访问 SharePoint 数据所需的范围和权限(例如,Sites.ReadWrite.All 以获得完全访问权限)。

    之后,只需使用生成并提供的 clientID、clientSecret、tenantID 和 scope 来请求新的访问令牌。

    我创建了一个类来为我执行所有 http 请求:

    public class GraphClient
    {
      private const string LOGIN_URL = "https://login.microsoftonline.com/{0}/oauth2/v2.0/token";
      private const string BASE_URL  = "https://graph.microsoft.com";
    
      protected internal string HttpBaseAddress { get; }
    
      protected internal readonly HttpClient HttpClient;
    
      public GraphClient(string tenantId, string clientId, string clientSecret, string version = "1.0")
      {
        var msgHandler = new GraphAuthMessageHandler(string.Format(LOGIN_URL, tenantId),
                                                             $"{BASE_URL}/.default",
                                                             clientId,
                                                             clientSecret,
                                                             new HttpClientHandler());
        HttpBaseAddress = $"{BASE_URL}/v{version}/";
        HttpClient = new HttpClient(msgHandler)
        {
          BaseAddress = new Uri(HttpBaseAddress),
          Timeout     = new TimeSpan(0, 2, 0)
        };
    
        HttpClient.DefaultRequestHeaders.Add("OData-MaxVersion", "4.0");
        HttpClient.DefaultRequestHeaders.Add("OData-Version", "4.0");
        HttpClient.DefaultRequestHeaders.Add("Prefer", "odata.include-annotations=\"*\"");
        HttpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
      }
    }
    

    提供的 MessageHangler 请求令牌并将提供的访问令牌添加到标头:

    using System.Collections.Generic;
    using System.Net.Http;
    using System.Net.Http.Headers;
    using System.Net.Http.Json;
    using System.Threading;
    using System.Threading.Tasks;
    
    public class GraphAuthMessageHandler : DelegatingHandler
    {
      private readonly string _loginUrl;
      private readonly string _clientId;
      private readonly string _clientSecret;
      private readonly string _scope;
    
      public GraphAuthMessageHandler(string loginUrl, string scope, string clientId, string clientSecret, HttpMessageHandler innerHandler)
                : base(innerHandler)
      {
        _loginUrl     = loginUrl;
        _clientId     = clientId;
        _clientSecret = clientSecret;
        _scope        = scope;
      }
    
      protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
      {
        var result = await AcquireAccessToken();
        request.Headers.Authorization = new AuthenticationHeaderValue(result.TokenType, result.AccessToken);
    
        return await base.SendAsync(request, cancellationToken);
      }
    
      private async Task<AuthResponse> AcquireAccessToken()
      {
        var httpClient = new HttpClient();
        var values = new List<KeyValuePair<string, string>>
        {
          new("client_id", _clientId),
          new("client_secret", _clientSecret),
          new("scope", _scope),
          new("grant_type", "client_credentials")
        };
    
        var response = await httpClient.PostAsync(_loginUrl, new FormUrlEncodedContent(values));
        return await response.Content.ReadFromJsonAsync<AuthResponse>();
      }
    }
    

    编辑这是 AuthResponse 类,它只是将 json 响应映射到 C# 对象:

    using System.Text.Json.Serialization;
    
    public class AuthResponse
    {
      [JsonPropertyName("token_type")]   public string TokenType   { get; set; }
      [JsonPropertyName("expires_in")]   public int    ExpiresIn   { get; set; }
      [JsonPropertyName("access_token")] public string AccessToken { get; set; }
    }
    

    然后简单地使用它:

    var driveId = "your-drive-id-here";
    var itemId = "your-item-id-here";
    var client = new GraphClient(TENANT_ID, CLIENT_ID, CLIENT_SECRET);
    
    var response = await client.HttpClient.GetAsync($"drives/{driveId}/items/{itemId}/content");
    
    if (response.IsSuccessStatusCode)
    {
      var fileStream = await response.Content.ReadAsStreamAsync();
      // do something with stream
    }
    
    // handle errors here
    

    Microsoft Docs 是让它工作的良好开端,它对我使用 Graph API 以及 Graph Explorer 来测试对端点的查询和请求有很大帮助。

    附:这只是一个简单的例子,当然还有改进的余地,但这有望为您指明正确的方向;)

    【讨论】:

    • 非常好的答案,谢谢!我只有 2 个愚蠢的问题,从 AcquireAccessToken() 方法返回的 AuthResponse 到底是什么?如何获取商品的 ID?
    • 我已经编辑了答案并添加了缺少的 AuthResponse 类,它只是将 json 响应映射到所需的属性。您可以通过在 Graph Explorer 中执行一些请求来获取 itemID。 OneDrive 下的左侧窗格中有几个模板请求,例如,要列出您自己的项目,它会向https://graph.microsoft.com/v1.0/me/drive/root/children 发出 GET 请求。 json 响应包含项目以及您将需要的 itemID。请。如果对您有帮助,请将答案标记为正确,谢谢:)
    • 我下周才回到这个项目,所以我无法确认它是否有效,但我会标记它,非常感谢
    猜你喜欢
    • 1970-01-01
    • 2019-09-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-17
    • 2017-06-15
    • 2014-02-11
    • 2023-03-20
    相关资源
    最近更新 更多