然后,我修改了 Startup.cs 文件。這是 asp.net core 應用程序的一個標準文件,用來定義程序入口,加載相關服務和中間件。(這里涉及的知識面太多,以至于我無法一一說明,有興趣可以參考?https://asp.net/core了解。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
//這里增加了一寫命名空間導入
using Microsoft.Extensions.Caching;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.AspNetCore.Session;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
using Microsoft.IdentityModel.Clients.ActiveDirectory;
using Microsoft.AspNetCore.Authentication;
using System.Security.Claims;
using Microsoft.AspNetCore.Http;namespace aspntecoremvc
{public class Startup{// This method gets called by the runtime. Use this method to add services to the container.//這里引入一些服務,注入一些組件public void ConfigureServices(IServiceCollection services){services.AddSession();services.AddAuthentication(sharedoptions => sharedoptions.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme);// Add framework services.services.AddMvc();}//這里定義了一些靜態信息private readonly string ClientId="e91ef175-e38d-4feb-b1ed-f243a6a81b93";private readonly string Authority=String.Format("https://login.microsoftonline.com/{0}","office365devlabs.onmicrosoft.com");private readonly string ClientSecret="2F5jdoGGNn59oxeDLE9fXx5tD86uvzIji74dmLaj3YI=";private readonly string GraphResourceId="https://graph.microsoft.com";private readonly string CallbackPath ="/signin-oidc";// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory){loggerFactory.AddConsole();loggerFactory.AddDebug();if (env.IsDevelopment()){app.UseDeveloperExceptionPage();app.UseBrowserLink();}else{app.UseExceptionHandler("/Home/Error");}app.UseStaticFiles();//這里幾步是最關鍵的,定義了如何進行身份認證以及如何保存app.UseSession();app.UseCookieAuthentication();app.UseOpenIdConnectAuthentication(new OpenIdConnectOptions{ClientId = ClientId,Authority = Authority,ClientSecret = ClientSecret,ResponseType = OpenIdConnectResponseType.CodeIdToken,CallbackPath = CallbackPath,GetClaimsFromUserInfoEndpoint =true,Events = new OpenIdConnectEvents{OnAuthorizationCodeReceived = OnAuthorizationCodeReceived}});app.UseMvc(routes =>{routes.MapRoute(name: "default",template: "{controller=Home}/{action=Index}/{id?}");});}private async Task OnAuthorizationCodeReceived(AuthorizationCodeReceivedContext context){// 將 Token 信息保存在 session 里面,后續 Graph 就可以直接調用了string userObjectId = (context.Ticket.Principal.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier"))?.Value;ClientCredential clientCred = new ClientCredential(ClientId, ClientSecret);AuthenticationContext authContext = new AuthenticationContext(Authority, new SampleSessionCache(userObjectId, context.HttpContext.Session));AuthenticationResult authResult = await authContext.AcquireTokenByAuthorizationCodeAsync(context.ProtocolMessage.Code, new Uri(context.Properties.Items[OpenIdConnectDefaults.RedirectUriForCodePropertiesKey]), clientCred, GraphResourceId);}private Task OnAuthenticationFailed(FailureContext context){context.HandleResponse();context.Response.Redirect("/Home/Error?message=" + context.Failure.Message);return Task.FromResult(0);}}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.IdentityModel.Clients.ActiveDirectory;
using Microsoft.Graph;
using System.Net.Http.Headers;
using Microsoft.AspNetCore.Http;namespace aspntecoremvc{public static class SDKHelper{//這里其實是對 ControllerBase 這個類型進行了擴展public static async Task<GraphServiceClient> GetAuthenticatedClient(this ControllerBase controller){var Authority = String.Format("https://login.microsoftonline.com/{0}","office365devlabs.onmicrosoft.com");var ClientId = "e91ef175-e38d-4feb-b1ed-f243a6a81b93";var ClientSecret = "2F5jdoGGNn59oxeDLE9fXx5tD86uvzIji74dmLaj3YI=";var GraphResourceId = "https://graph.microsoft.com";string userObjectId = controller.HttpContext.User.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier")?.Value;ClientCredential clientCred = new ClientCredential(ClientId, ClientSecret);AuthenticationContext authContext = new AuthenticationContext(Authority, new SampleSessionCache(userObjectId, controller.HttpContext.Session));AuthenticationResult result = await authContext.AcquireTokenSilentAsync(GraphResourceId,ClientId);GraphServiceClient client = new GraphServiceClient(new DelegateAuthenticationProvider(async request=>{request.Headers.Authorization = new AuthenticationHeaderValue("bearer", result.AccessToken);await Task.FromResult(0);}));return client;}}}
有了上面的準備,在真正需要用到 Graph 服務的地方,我們的代碼是非常簡單的,請參考 HomeController.cs 文件中的 About 方法
到這里為止,我的這個例子的主要代碼就解釋完了。你可能會覺得,這太簡單了吧。如果你這樣認為,我一方面感到很高興,因為這是我希望呈現出來的效果;另一方面我要提醒你的是,由于 asp.net core 是一個還比較新的技術,這方面的材料相當少,其實我還是做了相當多的研究才精煉成這樣的,其間遇到過多少坑,多少曲折迂回,不足以外人道也,但我很看好 asp.net core,并且將持續在此之上進行投資,這也幾乎是可以肯定的。
4結語
這個例子實現的功能并沒有什么驚天動地的,但與咱們之前一系列的范例相呼應的是,我是要幫助大家打開 Microsoft Graph 的大門,至于你要怎么去利用里面豐富的寶藏,我就選擇的權利交給你自己。
寫到這里,我的這個系列文章的第一個大的里程碑應該是要實現了。我用了將近四個月的時間,寫了十幾篇跟 Office 365 開發入門,以及具體的 Microsoft Graph 開發有關的文章,差不多算是比較完整了。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Caching;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.AspNetCore.Session;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
using Microsoft.IdentityModel.Clients.ActiveDirectory;
using Microsoft.AspNetCore.Authentication;
using System.Security.Claims;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;namespace Office365GraphCoreMVCHelper
{public class Startup{static IConfigurationRoot Configuration { get; set; }public Startup(IHostingEnvironment env){Configuration = new ConfigurationBuilder().SetBasePath(env.ContentRootPath).AddJsonFile("appsettings.json").Build();}// This method gets called by the runtime. Use this method to add services to the container.public void ConfigureServices(IServiceCollection services){//這里將配置信息注入到應用程序中services.AddOptions();services.Configure<AppSetting>(Configuration);services.AddSession();services.AddAuthentication(sharedoptions => sharedoptions.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme);// Add framework services.services.AddMvc();}// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory){loggerFactory.AddConsole();loggerFactory.AddDebug();if (env.IsDevelopment()){app.UseDeveloperExceptionPage();app.UseBrowserLink();}else{app.UseExceptionHandler("/Home/Error");}//ConfigureMiddleware(app,env,loggerFactory);app.UseStaticFiles();app.UseSession();app.UseCookieAuthentication();//獲得之前注入的配置信息var options = app.ApplicationServices.GetRequiredService<IOptions<AppSetting>>();app.UseOpenIdConnectAuthentication(new OpenIdConnectOptions{ClientId = options.Value.Office365ApplicationInfo.ClientId,Authority = options.Value.Office365ApplicationInfo.Authority,ClientSecret = options.Value.Office365ApplicationInfo.ClientSecret,ResponseType = OpenIdConnectResponseType.CodeIdToken,CallbackPath = "/signin-oidc",GetClaimsFromUserInfoEndpoint = true,Events = new OpenIdConnectEvents{OnAuthorizationCodeReceived = async (context) =>{string userObjectId = (context.Ticket.Principal.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier"))?.Value;ClientCredential clientCred = new ClientCredential(options.Value.Office365ApplicationInfo.ClientId, options.Value.Office365ApplicationInfo.ClientSecret);AuthenticationContext authContext = new AuthenticationContext(options.Value.Office365ApplicationInfo.Authority, new SampleSessionCache(userObjectId, context.HttpContext.Session));AuthenticationResult authResult = await authContext.AcquireTokenByAuthorizationCodeAsync(context.ProtocolMessage.Code, new Uri(context.Properties.Items[OpenIdConnectDefaults.RedirectUriForCodePropertiesKey]), clientCred, options.Value.Office365ApplicationInfo.GraphResourceId);}}});app.UseMvc(routes =>{routes.MapRoute(name: "default",template: "{controller=Home}/{action=Index}/{id?}");});}private Task OnAuthenticationFailed(FailureContext context){context.HandleResponse();context.Response.Redirect("/Home/Error?message=" + context.Failure.Message);return Task.FromResult(0);}}
}
改造過的SDKHelper類型,主要增加了從配置文件中讀取信息的功能
using System;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using Microsoft.Graph;
using Microsoft.IdentityModel.Clients.ActiveDirectory;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using Microsoft.AspNetCore.Builder;namespace Office365GraphCoreMVCHelper
{public static class SDKHelper{public static async Task<GraphServiceClient> GetAuthenticatedClient(this ControllerBase controller,IOptions<AppSetting> options){var Authority = options.Value.Office365ApplicationInfo.Authority;var ClientId = options.Value.Office365ApplicationInfo.ClientId;var ClientSecret = options.Value.Office365ApplicationInfo.ClientSecret;var GraphResourceId = options.Value.Office365ApplicationInfo.GraphResourceId;string userObjectId = controller.HttpContext.User.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier")?.Value;ClientCredential clientCred = new ClientCredential(ClientId, ClientSecret);AuthenticationContext authContext = new AuthenticationContext(Authority, new SampleSessionCache(userObjectId, controller.HttpContext.Session));AuthenticationResult result = await authContext.AcquireTokenSilentAsync(GraphResourceId, ClientId);GraphServiceClient client = new GraphServiceClient(new DelegateAuthenticationProvider(async request =>{request.Headers.Authorization = new AuthenticationHeaderValue("bearer", result.AccessToken);await Task.FromResult(0);}));return client;}}
}
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
using Office365GraphCoreMVCHelper;namespace aspntecoremvc
{public class Program{public static void Main(string[] args){var host = new WebHostBuilder().UseSetting("startupAssembly","Office365GraphCoreMVCHelper").UseKestrel().UseContentRoot(Directory.GetCurrentDirectory()).UseIISIntegration().Build();host.Run();}}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.IdentityModel.Clients.ActiveDirectory;
using Microsoft.Graph;
using System.Net.Http.Headers;
using Office365GraphCoreMVCHelper;
using Microsoft.Extensions.Options;namespace aspntecoremvc.Controllers
{public class HomeController : Controller{private readonly IOptions<AppSetting> Options;public HomeController(IOptions<AppSetting> options){this.Options = options;}public IActionResult Index(){return View();}[Authorize]public async Task<IActionResult> About(){var client = await this.GetAuthenticatedClient(this.Options); ? ? ? ? ? ?return View(await client.Me.Request().GetAsync());}public IActionResult Contact(){ViewData["Message"] = "Your contact page.";return View();}public IActionResult Error(){return View();}}
}
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;namespace testaspnetcoremvc
{public class Program{public static void Main(string[] args){var host = new WebHostBuilder().UseKestrel().UseContentRoot(Directory.GetCurrentDirectory()).UseIISIntegration().UseSetting("startupAssembly","Office365GraphCoreMVCHelper").Build();host.Run();}}
}