zoukankan      html  css  js  c++  java
  • Azure AD, Endpoint Manger(Intune), SharePoint access token 的获取

    本章全是干货干货干货,重要的事情说三遍。

       最近在研究Azure, Cloud相关的东西,项目中用的是Graph API(这个在下一章会相信介绍),可能是Graph API推出的时间比较晚,部分API还在开发中,所以难免出现部分功能支持不完善,issue之类的,当然Microsoft也在不断更新完善中,期待Graph API时代的到来。

       针对以上的问题,有时在项目中不得不采取一些非常规的手段,调用Microsoft backend API,什么是backend API呢,就是我们用Fiddle监控或者页面F12 Network 看到Microsoft调用的底层Rest API。

        例如:Azure AD的Company Branding功能,Graph V 1.0版本没有API支持,但项目需要,怎么办?

         1)、查找是否有Beta版本,如果有,项目组决定是否可以使用Beta API ,因为Microsoft 官网是不建议Beta API应用于生产环境的。

         https://docs.microsoft.com/en-us/graph/api/resources/organizationalbrandingproperties?view=graph-rest-beta

         2)、我们今天说的重点,调用Microsoft backend API

    首先说明一下啊,所有获取token的token endpoint都是统一地址:当然这里的common理论上是应该换成 tenantId的,这样可以提高获取token的效率,原因是Microsoft会根据你的tenantId 所在的region,请求最近DataCenter的服务器。

    private string _tokenEndpoint = "https://login.microsoftonline.com/common/oauth2/token";

    1、 Backend API Access Token

    下图是Azure AD的Company branding,通常每个tenantId会根据自己的喜好设置登录页面的logo,Sign in text等信息,也是一个比较常用的需求。废话有点多了,咱们回到正题,如下图所示,我们会发现Microsoft当前用的正是Rest API,尽管Microsoft自己已经推出了Beta API,但自己却不用,为何???值得思考哈

    划重点,调用backend api,最重要是获取access token,怎样获取access token呢,这里是impersonate user login(模拟用户登录的方式)。

    private async Task<string> GetAADAccessToken(string username, string password)
    {
         var resource = $"resource=74658136-14ec-4630-ad9b-26e160ff0fc6&client_id=1950a258-227b-4e31-a9cf-717495945fc2&grant_type=password&username={username}&password={password}";
         return await GetAccessToken(resource);
    }
    
    private async Task<string> GetAccessToken(string resource)
    {
         var httpClient = new HttpClient();
         using (var stringContent = new StringContent(resource, Encoding.UTF8, "application/x-www-form-urlencoded"))
         {
                    try
                    {
                        var result = await httpClient.PostAsync(_tokenEndpoint, stringContent).ContinueWith((response) =>
                        {
                            return response.Result.Content.ReadAsStringAsync().Result;
                        }).ConfigureAwait(false);
    
                        var tokenResult = JsonSerializer.Deserialize<JsonElement>(result);
                        var token = tokenResult.GetProperty("access_token").GetString();
                        return token;
                    }
                    catch (Exception ex)
                    {
                        throw new TokenException(ErrorCode.BadRequest, ex.Message);
                    }
         }
    }

    解释一下哈:

    “1950a258-227b-4e31-a9cf-717495945fc2” 是Microsoft build-in的application Id,app 名字是"Powershell ***", 从名字可得知这个application Id同样适用于Powershell。

    “74658136-14ec-4630-ad9b-26e160ff0fc6” 是token的颁发地址。

    以上两个ID是固定值,直接使用就可以。

    获取到了access  token,接下需要构建HTTP Client,示例如下:

    private HttpClient BuildHttpClient(string accessToken)
    {
                var httpClient = new HttpClient();
                httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
                httpClient.DefaultRequestHeaders.Add("X-Requested-With", "XMLHttpRequest");
                httpClient.DefaultRequestHeaders.Add("x-ms-client-request-id", $"{new Guid()}");
                httpClient.DefaultRequestHeaders.Add("x-ms-correlation-id", $"{new Guid()}");
                return httpClient;
    }

    最后发送HTTP请求,获取数据信息:

    private static IEnumerable<CompanyBrandingModel> GetCompanyBrandings()
    {
                var services = new ServiceCollection();
                var provider = services.ConfigureServices().BuildServiceProvider();
                var httpClientService = provider.GetService<IHttpClientService>();
                var httpClient = httpClientService?.GetHttpClient(_username, _password, TokenType.AzureAD, _clientId).GetAwaiter().GetResult();
                
                var requestUrl = "https://main.iam.ad.ext.azure.com/api/LoginTenantBrandings";
                var response = httpClient?.GetAsync(requestUrl).GetAwaiter().GetResult();            
                var content = response?.Content.ReadAsStringAsync().Result;
                var brandings = JsonConvert.DeserializeObject<List<CompanyBrandingModel>>(content)
                    .Where(b => b.isConfigured.HasValue && b.isConfigured.Value);
                return brandings;
    
    }

    获取的结果和页面一致,证明backend API还是可行的,哈哈。

     2、Graph Token

      Graph API官网为我们提供了多种方式使用Graph API:

      1、通过Nuget 安装Graph SDK,调用Graph SDK

       

      构建GraphServiceClient对象,这里提供了2种方式,Delegated 和 Application,Application又分Secret 和 Certification,我们这里只介绍Application Secret。

     public GraphServiceClient GetGraphServiceClientByApp(string tenantId, string clientId, string clientSecret)
     {
                var confidentialClientApplication = ConfidentialClientApplicationBuilder
                    .Create(clientId)
                    .WithTenantId(tenantId)
                    .WithClientSecret(clientSecret)
                    .Build();
                var credentialProvider = new ClientCredentialProvider(confidentialClientApplication);
                var graphClient = new GraphServiceClient(credentialProvider);
                return graphClient;
    }

    2、调用Graph Rest API,首先需要获取Graph access token,示例代码如下:

    public async Task<string> GetAppAccessToken(string clientId, string clientSecret)
    {
         if (string.IsNullOrEmpty(clientId) || string.IsNullOrEmpty(clientSecret))
         {
           throw new TokenException(ErrorCode.InvalidArguments, ExceptionConstants.NullOrEmpty);
         }
         var resource = $"scope=https://graph.microsoft.com/.default&grant_type=client_credentials&client_id={clientId}&client_secret={clientSecret}";
         return await GetAccessToken(resource);
    }

    有了access token之后,就可以构建http client,发送http请求了,同上,这里就不在累述了,说多了都是废话,标题就是干货干货干货

     3、SharePoint access token

      操作SharePoint的API有好多种,这里咱们只说Asp.NET Core CSOM, .NET Framework比较简单,这里就不多说了,想了解的下方留言吧。

      首先获取SharePoint access token,直接干代码:

    private async Task<string> GetSPAccessToken(string username, string password, string clientId)
    {
                var spAdminUrl = $"https://{username.Substring(username.IndexOf("@") + 1, username.IndexOf(".") - username.IndexOf("@") - 1)}-admin.sharepoint.com/";
                try
                {
                    var uri = new Uri(spAdminUrl);
                    string _resource = $"{uri.Scheme}://{uri.DnsSafeHost}";
                    var resource = $"resource={_resource}&client_id={clientId}&grant_type=password&username={HttpUtility.UrlEncode(username)}&password={HttpUtility.UrlEncode(password)}";
                    return await GetAccessToken(resource);
                }
                catch (System.UriFormatException e)
                {
                    throw new UriFormatException(e.Message, e);
                }
    }

     拿到token之后,通过Nuget安装package,构建ClientContext对象:

    public class SPClientContextService
    {
            private readonly ITokenCacheService _tokenCacheService;
    
            public SPClientContextService(ITokenCacheService tokenCacheService)
            {
                this._tokenCacheService = tokenCacheService;
            }
    
            public async Task<ClientContext> GetSPClientContextInstance(string username, string password, string clientId)
            {
                var accessToken = await _tokenCacheService.TryGetValue(username, password, TokenType.SharePoint, clientId);
                var spAdminUrl = $"https://{username.Substring(username.IndexOf("@") + 1, username.IndexOf(".") - username.IndexOf("@") - 1)}-admin.sharepoint.com/";
                var context = new ClientContext(new Uri(spAdminUrl));
                context.ExecutingWebRequest += (sender, e) =>
                {
                    e.WebRequestExecutor.RequestHeaders["Authorization"] = "Bearer " + accessToken;
                };
                return context;
            }
     }

     补充20210505

    上面操作SharePoint Online 用的是username, password及clientId的方式获取access token,之后构建一个ClientContext通过CSOM API操作SharePoint。这也是官网对SharePoint在net standard下示例推荐。

    但这里需要说明的是,目前CSOM不支持 clientSecret的方式操作SharePoint,仅支持Certificator验证

    var tokenEndpoint = $"https://login.microsoftonline.com/{tenantId}/oauth2/token";
    var content = $"resource={System.Web.HttpUtility.UrlEncode(resource)}&grant_type=client_credentials&client_id={clientId}&client_secret={clientSecret}";

    V2.0写法:

    var tokenEndpoint = $"https://login.microsoftonline.com/{tenantId}/oauth2/v2.0/token";
    var content = $"scope={System.Web.HttpUtility.UrlEncode(resource)}/.default&grant_type=client_credentials&client_id={clientId}&client_secret={clientSecret}";

    OK,篇幅已经比较长了,如需了解更多,请在下方留言,源码请参见 Github

  • 相关阅读:
    使用protobuf生成代码import包找不到
    SQL 对邮箱数据的处理(分类统计)
    Hive 集合函数 collect_set() collect_list()
    菜谱分享网站微信小程序开发说明(1)-介绍与运行
    windows 下查看端口占用
    Windows下安装Maven自定义仓库配置阿里下载源,配置Ecplise、IDEA
    Windows下安装Node.js完整详细教程
    开启Centos8的SSH服务
    Openwrt与IPTV之二----udpxy
    Openwrt与IPTV之一----igmpproxy
  • 原文地址:https://www.cnblogs.com/qindy/p/14517352.html
Copyright © 2011-2022 走看看