以下代码 (.NET 6) 适用于 Graph v1.0。
using Azure;
using Azure.Identity;
using Azure.Security.KeyVault.Secrets;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.Authorization;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Identity.Web;
using Microsoft.Identity.Web.UI;
using System;
namespace Birthway
{
public class Startup
{
public IConfiguration Configuration { get; }
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
string[] initialScopes = Configuration.GetValue<string>("DownstreamApi:Scopes")?.Split(' ');
services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp(Configuration)
.EnableTokenAcquisitionToCallDownstreamApi(initialScopes)
.AddMicrosoftGraph(Configuration.GetSection("DownstreamApi"))
.AddInMemoryTokenCaches();
// uncomment the following 3 lines to get ClientSecret from KeyVault
//string tenantId = Configuration.GetValue<string>("AzureAd:TenantId");
//services.Configure<MicrosoftIdentityOptions>(
// options => { options.ClientSecret = GetSecretFromKeyVault(tenantId, "ENTER_YOUR_SECRET_NAME_HERE"); });
services.AddControllersWithViews(options =>
{
var policy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
options.Filters.Add(new AuthorizeFilter(policy));
});
services.AddRazorPages()
.AddMicrosoftIdentityUI();
// Add the UI support to handle claims challenges
services.AddServerSideBlazor()
.AddMicrosoftIdentityConsentHandler();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
endpoints.MapRazorPages();
});
}
/// Gets the secret from key vault via an enabled Managed Identity.
/// </summary>
/// <remarks>https://github.com/Azure-Samples/app-service-msi-keyvault-dotnet/blob/master/README.md</remarks>
/// <returns></returns>
private string GetSecretFromKeyVault(string tenantId, string secretName)
{
// this should point to your vault's URI, like https://<yourkeyvault>.vault.azure.net/
string uri = Environment.GetEnvironmentVariable("KEY_VAULT_URI");
DefaultAzureCredentialOptions options = new DefaultAzureCredentialOptions();
// Specify the tenant ID to use the dev credentials when running the app locally
options.VisualStudioTenantId = tenantId;
options.SharedTokenCacheTenantId = tenantId;
SecretClient client = new SecretClient(new Uri(uri), new DefaultAzureCredential(options));
// The secret name, for example if the full url to the secret is https://<yourkeyvault>.vault.azure.net/secrets/ENTER_YOUR_SECRET_NAME_HERE
Response<KeyVaultSecret> secret = client.GetSecretAsync(secretName).Result;
return secret.Value.Value;
}
}
}
using Azure.Identity;
using Azure.Security.KeyVault.Secrets;
using Azure;
using Birthway.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Graph;
using Microsoft.Graph.Beta;
using Microsoft.Identity.Client;
using Microsoft.Identity.Web;
using System.Diagnostics;
using Microsoft.Graph.Models;
using Microsoft.Kiota.Abstractions;
namespace Birthway.Controllers
{
[Authorize]
public class HomeController : Controller
{
private readonly ILogger<HomeController> _logger;
private readonly Microsoft.Graph.GraphServiceClient _graphServiceClient;
private readonly MicrosoftIdentityConsentAndConditionalAccessHandler _consentHandler;
private readonly string[] _graphScopes;
public HomeController(ILogger<HomeController> logger, IConfiguration configuration, Microsoft.Graph.GraphServiceClient graphServiceClient,
MicrosoftIdentityConsentAndConditionalAccessHandler consentHandler)
{
_logger = logger;
_graphServiceClient = graphServiceClient;
_consentHandler = consentHandler;
_graphScopes = configuration.GetValue<string>("DownstreamApi:Scopes")?.Split(' ');
}
[AuthorizeForScopes(ScopeKeySection = "DownstreamApi:Scopes")]
public async Task<IActionResult> Index()
{
User currentUser = null;
try
{
currentUser = await _graphServiceClient.Me.GetAsync();
}
// Catch CAE exception from Graph SDK
catch (ServiceException svcex) when (svcex.Message.Contains("Continuous access evaluation resulted in claims challenge"))
{
try
{
Console.WriteLine($"{svcex}");
string claimChallenge = WwwAuthenticateParameters.GetClaimChallengeFromResponseHeaders(svcex.ResponseHeaders);
_consentHandler.ChallengeUser(_graphScopes, claimChallenge);
return new EmptyResult();
}
catch (Exception ex2)
{
_consentHandler.HandleException(ex2);
}
}
try
{
// Get user photo
using (var photoStream = await _graphServiceClient.Me.Photo.Content.GetAsync())
{
byte[] photoByte = ((MemoryStream)photoStream).ToArray();
ViewData["Photo"] = Convert.ToBase64String(photoByte);
}
}
catch (Exception pex)
{
Console.WriteLine($"{pex.Message}");
ViewData["Photo"] = null;
}
ViewData["Me"] = currentUser;
return View();
}
}
}
我需要它与 Graph beta 一起使用,这就是为什么我将 BaseUrl 值更改为“https://graph.microsoft.com/beta”(appsettings.json),这次使用 Microsoft.Graph 实例化 GraphServiceClient 对象。 Beta 版并改编代码 (HomeController.cs)。它似乎在容器部分失败,但我不明白为什么。
我的改变:
using Birthway.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Graph;
using Microsoft.Graph.Beta;
using Microsoft.Identity.Client;
using Microsoft.Identity.Web;
using System.Diagnostics;
using Microsoft.Graph.Models;
using Microsoft.Kiota.Abstractions;
namespace Birthway.Controllers
{
[Authorize]
public class HomeController : Controller
{
private readonly ILogger<HomeController> _logger;
private readonly Microsoft.Graph.Beta.GraphServiceClient _graphServiceClient;
private readonly MicrosoftIdentityConsentAndConditionalAccessHandler _consentHandler;
private readonly string[] _graphScopes;
public HomeController(ILogger<HomeController> logger, IConfiguration configuration, Microsoft.Graph.Beta.GraphServiceClient graphServiceClient,
MicrosoftIdentityConsentAndConditionalAccessHandler consentHandler)
{
_logger = logger;
_graphServiceClient = graphServiceClient;
_consentHandler = consentHandler;
_graphScopes = configuration.GetValue<string>("DownstreamApi:Scopes")?.Split(' ');
}
[AuthorizeForScopes(ScopeKeySection = "DownstreamApi:Scopes")]
public async Task<IActionResult> Index()
{
Microsoft.Graph.Beta.Models.User currentUser = null;
try
{
currentUser = await _graphServiceClient.Me.GetAsync();
}
// Catch CAE exception from Graph SDK
catch (ServiceException svcex) when (svcex.Message.Contains("Continuous access evaluation resulted in claims challenge"))
{
try
{
Console.WriteLine($"{svcex}");
string claimChallenge = WwwAuthenticateParameters.GetClaimChallengeFromResponseHeaders(svcex.ResponseHeaders);
_consentHandler.ChallengeUser(_graphScopes, claimChallenge);
return new EmptyResult();
}
catch (Exception ex2)
{
_consentHandler.HandleException(ex2);
}
}
try
{
// Get user photo
using (var photoStream = await _graphServiceClient.Me.Photo.Content.GetAsync())
{
byte[] photoByte = ((MemoryStream)photoStream).ToArray();
ViewData["Photo"] = Convert.ToBase64String(photoByte);
}
}
catch (Exception pex)
{
Console.WriteLine($"{pex.Message}");
ViewData["Photo"] = null;
}
ViewData["Me"] = currentUser;
return View();
}
}
}
错误: