• .NET 5 获得SharePoint Online的Client Context


    当使用.NET core 或者.NET 5时,安装完SharePoint Online CSOM 包以后,需要获得Client Context。发现SharePointOnlineCredentials class不可用了。因为微软已经删除了这个class。https://docs.microsoft.com/en-us/answers/questions/58183/net-core-sharepointonlinecredentials-class.html

    微软网站也给出了解决方案,就是重写AuthenticationManager。我参照上面的方法,重写了通过App Only获得Client Context的方法。废话不多说,上代码。

    AuthenticationManager.cs

      1 using Microsoft.SharePoint.Client;
      2 using System;
      3 using System.Collections.Concurrent;
      4 using System.Net.Http;
      5 using System.Text;
      6 using System.Text.Json;
      7 using System.Threading;
      8 using System.Threading.Tasks;
      9 
     10 namespace DownloadSPLibAndUploadToAzureBlobWithNet5
     11 {
     12     public class AuthenticationManager : IDisposable
     13     {
     14         private static readonly HttpClient httpClient = new HttpClient();
     15         private const string tenantName = "";  //tenant name or id.
     16 
     17         // Token cache handling
     18         private static readonly SemaphoreSlim semaphoreSlimTokens = new SemaphoreSlim(1);
     19         private AutoResetEvent tokenResetEvent = null;
     20         private readonly ConcurrentDictionary<string, string> tokenCache = new ConcurrentDictionary<string, string>();
     21         private bool disposedValue;
     22 
     23 
     24         internal class TokenWaitInfo
     25         {
     26             public RegisteredWaitHandle Handle = null;
     27         }
     28 
     29         public ClientContext GetContext(Uri web, string clientId, string clientSecret)
     30         {
     31             ClientContext context = new ClientContext(web);
     32             context.ExecutingWebRequest += (sender, e) =>
     33             {
     34                 string accessToken = EnsureAccessTokenAsync(web, clientId, clientSecret).GetAwaiter().GetResult();
     35                 e.WebRequestExecutor.RequestHeaders["Authorization"] = "Bearer " + accessToken;
     36             };
     37 
     38             return context;
     39         }
     40 
     41         public async Task<string> EnsureAccessTokenAsync(Uri web, string clientId, string clientSecret)
     42         {
     43             string accessTokenFromCache = TokenFromCache(web, tokenCache);
     44             if (accessTokenFromCache == null)
     45             {
     46                 await semaphoreSlimTokens.WaitAsync().ConfigureAwait(false);
     47                 try
     48                 {
     49                     // No async methods are allowed in a lock section
     50                     string accessToken = await AcquireTokenAsync(web, clientId, clientSecret).ConfigureAwait(false);
     51                     AddTokenToCache(web, tokenCache, accessToken);
     52 
     53                     // Register a thread to invalidate the access token once's it's expired
     54                     tokenResetEvent = new AutoResetEvent(false);
     55                     TokenWaitInfo wi = new TokenWaitInfo();
     56                     wi.Handle = ThreadPool.RegisterWaitForSingleObject(
     57                         tokenResetEvent,
     58                         async (state, timedOut) =>
     59                         {
     60                             if (!timedOut)
     61                             {
     62                                 TokenWaitInfo wi = (TokenWaitInfo)state;
     63                                 if (wi.Handle != null)
     64                                 {
     65                                     wi.Handle.Unregister(null);
     66                                 }
     67                             }
     68                             else
     69                             {
     70                                 try
     71                                 {
     72                                     // Take a lock to ensure no other threads are updating the SharePoint Access token at this time
     73                                     await semaphoreSlimTokens.WaitAsync().ConfigureAwait(false);
     74                                     RemoveTokenFromCache(web, tokenCache);
     75                                     Console.WriteLine($"Cached token for resource {web.DnsSafeHost} and clientId {clientId} expired");
     76                                 }
     77                                 catch (Exception ex)
     78                                 {
     79                                     Console.WriteLine($"Something went wrong during cache token invalidation: {ex.Message}");
     80                                     RemoveTokenFromCache(web, tokenCache);
     81                                 }
     82                                 finally
     83                                 {
     84                                     semaphoreSlimTokens.Release();
     85                                 }
     86                             }
     87                         },
     88                         wi,
     89                         (uint)CalculateThreadSleep(accessToken).TotalMilliseconds,
     90                         true
     91                     );
     92 
     93                     return accessToken;
     94                 }
     95                 finally
     96                 {
     97                     semaphoreSlimTokens.Release();
     98                 }
     99             }
    100             else
    101                 return accessTokenFromCache;
    102         }
    103 
    104         private async Task<string> AcquireTokenAsync(Uri web, string clientId, string clientSecret)
    105         {
    106             var body = "grant_type=client_credentials" +
    107                 $"&resource=00000003-0000-0ff1-ce00-000000000000/{web.DnsSafeHost}@{tenantName}" +
    108                 $"&client_id={clientId}@{tenantName}" +
    109                 $"&client_secret={clientSecret}";
    110 
    111             using (var stringContent = new StringContent(body, Encoding.UTF8, "application/x-www-form-urlencoded"))
    112             {
    113                 var result = await httpClient.PostAsync($"https://accounts.accesscontrol.windows.net/{tenantName}/tokens/OAuth/2", stringContent)
    114                     .ContinueWith((response) =>
    115                     {
    116                         return response.Result.Content.ReadAsStringAsync().Result;
    117                     })
    118                     .ConfigureAwait(false);
    119 
    120                 var tokenResult = JsonSerializer.Deserialize<JsonElement>(result);
    121                 var token = tokenResult.GetProperty("access_token").GetString();
    122                 return token;
    123             }
    124         }
    125 
    126         private static string TokenFromCache(Uri web, ConcurrentDictionary<string, string> tokenCache)
    127         {
    128             if (tokenCache.TryGetValue(web.DnsSafeHost, out string accessToken))
    129                 return accessToken;
    130 
    131             return null;
    132         }
    133 
    134         private static void AddTokenToCache(Uri web, ConcurrentDictionary<string, string> tokenCache, string newAccessToken)
    135         {
    136             if (tokenCache.TryGetValue(web.DnsSafeHost, out string currentAccessToken))
    137                 tokenCache.TryUpdate(web.DnsSafeHost, newAccessToken, currentAccessToken);
    138             else
    139                 tokenCache.TryAdd(web.DnsSafeHost, newAccessToken);
    140         }
    141 
    142         private static void RemoveTokenFromCache(Uri web, ConcurrentDictionary<string, string> tokenCache)
    143         {
    144             tokenCache.TryRemove(web.DnsSafeHost, out string currentAccessToken);
    145         }
    146 
    147         private static TimeSpan CalculateThreadSleep(string accessToken)
    148         {
    149             var token = new System.IdentityModel.Tokens.Jwt.JwtSecurityToken(accessToken);
    150             var lease = GetAccessTokenLease(token.ValidTo);
    151             lease = TimeSpan.FromSeconds(lease.TotalSeconds - TimeSpan.FromMinutes(5).TotalSeconds > 0 ? lease.TotalSeconds - TimeSpan.FromMinutes(5).TotalSeconds : lease.TotalSeconds);
    152             return lease;
    153         }
    154 
    155         private static TimeSpan GetAccessTokenLease(DateTime expiresOn)
    156         {
    157             DateTime now = DateTime.UtcNow;
    158             DateTime expires = expiresOn.Kind == DateTimeKind.Utc ? expiresOn : TimeZoneInfo.ConvertTimeToUtc(expiresOn);
    159             TimeSpan lease = expires - now;
    160             return lease;
    161         }
    162         protected virtual void Dispose(bool disposing)
    163         {
    164             if (!disposedValue)
    165             {
    166                 if (disposing)
    167                 {
    168                     if (tokenResetEvent != null)
    169                     {
    170                         tokenResetEvent.Set();
    171                         tokenResetEvent.Dispose();
    172                     }
    173                 }
    174                 disposedValue = true;
    175             }
    176         }
    177 
    178         public void Dispose()
    179         {
    180             // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
    181             Dispose(disposing: true);
    182             GC.SuppressFinalize(this);
    183         }
    184     }
    185 }

    program.cs

     1 using Microsoft.SharePoint.Client;
     2 using System;
     3 using System.Threading.Tasks;
     4 
     5 namespace DownloadSPLibAndUploadToAzureBlobWithNet5
     6 {
     7     class Program
     8     {
     9         static async Task Main(string[] args)
    10         {
    11             Uri site = new Uri("https://******.sharepoint.com/sites/10000034");
    12             string clientId = "";
    13             string clientSecret = "";
    14 
    15             // Note: The PnP Sites Core AuthenticationManager class also supports this
    16             using (var authenticationManager = new AuthenticationManager())
    17             {
    18                 using (var context = authenticationManager.GetContext(site, clientId, clientSecret))
    19                 {
    20                     context.Load(context.Web, p => p.Title);
    21                     await context.ExecuteQueryAsync();
    22                     Console.WriteLine($"Title: {context.Web.Title}");
    23 
    24                     var list = context.Web.Lists.GetByTitle("Links");
    25                     ListItemCreationInformation itemCreateInfo = new ListItemCreationInformation();
    26                     ListItem newItem = list.AddItem(itemCreateInfo);
    27                     newItem["Title"] = "My New Item!";
    28                     newItem.Update();
    29                     await context.ExecuteQueryAsync();
    30                     Console.WriteLine(newItem.Id);
    31                 }
    32             }
    33 
    34         }
    35     }
    36 }

    通过用户名和密码获得client context可以直接参考微软给的code. https://docs.microsoft.com/en-us/sharepoint/dev/sp-add-ins/using-csom-for-dotnet-standard

    另外还有一种更简便的方发,就是使用pnp.framework包。

    用VS生成新项目以后,安装pnp.framework包,然后用下面的code就可以得到client context了。

     1 using Microsoft.SharePoint.Client;
     2 using System;
     3 using System.Threading.Tasks;
     4 
     5 namespace DownloadSPLibAndUploadToAzureBlobWithNet5
     6 {
     7     class Program
     8     {
     9         static async Task Main(string[] args)
    10         {
    11             Uri site = new Uri("https://********.sharepoint.com/sites/10000034");
    12             string clientId = "";
    13             string clientSecret = "";
    14 
    15             // Note: The PnP Sites Core AuthenticationManager class also supports this
    16             using (var authenticationManager = new AuthenticationManager())
    17             {
    18                 using (var context = authenticationManager.GetContext(site, clientId, clientSecret))
    19                 {
    20                     context.Load(context.Web, p => p.Title);
    21                     await context.ExecuteQueryAsync();
    22                     Console.WriteLine($"Title: {context.Web.Title}");
    23 
    24                     var list = context.Web.Lists.GetByTitle("Links");
    25                     ListItemCreationInformation itemCreateInfo = new ListItemCreationInformation();
    26                     ListItem newItem = list.AddItem(itemCreateInfo);
    27                     newItem["Title"] = "My New Item!";
    28                     newItem.Update();
    29                     await context.ExecuteQueryAsync();
    30                     Console.WriteLine(newItem.Id);
    31                 }
    32             }
    33 
    34         }
    35     }
    36 }

     github 链接: https://github.com/HaiyeWang0717/DownloadSPLibAndUploadToAzureBlobWithNet5

  • 相关阅读:
    JZOJ1495 宝石
    JZOJ1496 页
    jzoj1497. 景点中心
    2019.8.2总结
    学习进度报告2021/3/19
    学习进度报告2021/3/18
    《学会提问》读书笔记2
    学习进度报告2021/3/17
    学习进度报告2021/3/16
    学习进度报告2021/3/15
  • 原文地址:https://www.cnblogs.com/sharepointonline/p/14707280.html
Copyright © 2020-2023  润新知