Notatka
Dostęp do tej strony wymaga autoryzacji. Może spróbować zalogować się lub zmienić katalogi.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
Dodaj nowoczesne uwierzytelnianie do aplikacji ASP.NET MVC i Web API w .NET Framework 4.7.2 lub nowszym, korzystając z pakietu Microsoft.Identity.Web.OWIN.
Omówienie integracji OWIN
Microsoft.Identity.Web.OWIN zapewnia możliwości Microsoft.Identity.Web dla aplikacji ASP.NET MVC i aplikacji Web API, które korzystają z oprogramowania pośredniczącego OWIN oraz Microsoft Entra ID.
Zapoznaj się z kluczowymi korzyściami
Poniższa tabela zawiera podsumowanie najważniejszych funkcji i korzyści pakietu.
| Funkcja | Korzyść |
|---|---|
| TokenAcquirerFactory | Automatyczne pozyskiwanie tokenów za pomocą buforowania |
| Rozszerzenia kontrolera | Łatwy dostęp do GraphServiceClient i IDownstreamApi |
| Rozproszona pamięć podręczna tokenów | Wbudowana obsługa SQL Server, Redis, Cosmos DB, PostgreSQL |
| Automatyczne odświeżanie tokenu | Obsługuje przezroczyste odświeżanie tokenu |
| Zgoda przyrostowa | Bezproblemowa integracja przepływu zgody |
Przegląd obsługiwanych scenariuszy
Microsoft.Identity.Web.OWIN obsługuje następujące typy aplikacji i scenariusze.
- ASP.NET MVC Web Applications (.NET Framework 4.7.2+)
- ASP.NET Web API (.NET Framework 4.7.2+)
- Aplikacje hybrydowe (MVC i internetowy interfejs API)
- Calling Microsoft Graph z kontrolerów
- Wywoływanie podrzędnych interfejsów API z automatycznym uwierzytelnianiem
Instalowanie pakietu
Zainstaluj pakiet Microsoft.Identity.Web.OWIN NuGet przy użyciu preferowanej przez Ciebie metody.
Uruchom następujące polecenie w konsoli Menedżer pakietów:
Install-Package Microsoft.Identity.Web.OWIN
Alternatywnie uruchom następujące polecenie używając .NET CLI:
dotnet add package Microsoft.Identity.Web.OWIN
Zależności są automatycznie uwzględniane:
- Microsoft.Identity.Web.TokenAcquisition
- Microsoft. Identity.Web.TokenCache
- Microsoft.Owin
- System.web
Konfigurowanie aplikacji
Skonfiguruj ustawienia aplikacji, aby łączyć się z interfejsami API Microsoft Entra i podrzędnymi.
Konfigurowanie Web.config
Dodaj do pliku Web.config następujące ustawienia Microsoft Entra i ustawienia interfejsu API downstream.
<configuration>
<appSettings>
<!-- Microsoft Entra ID Configuration -->
<add key="AzureAd:Instance" value="https://login.microsoftonline.com/" />
<add key="AzureAd:TenantId" value="your-tenant-id" />
<add key="AzureAd:ClientId" value="your-client-id" />
<add key="AzureAd:ClientSecret" value="your-client-secret" />
<add key="AzureAd:RedirectUri" value="https://localhost:44368/" />
<add key="AzureAd:PostLogoutRedirectUri" value="https://localhost:44368/" />
<!-- Microsoft Graph Configuration -->
<add key="DownstreamApi:MicrosoftGraph:BaseUrl" value="https://graph.microsoft.com/v1.0" />
<add key="DownstreamApi:MicrosoftGraph:Scopes" value="user.read" />
<!-- Custom Downstream API Configuration -->
<add key="DownstreamApi:TodoListService:BaseUrl" value="https://localhost:44351" />
<add key="DownstreamApi:TodoListService:Scopes" value="api://todo-api-client-id/.default" />
</appSettings>
<connectionStrings>
<!-- Optional: SQL Server Token Cache -->
<add name="TokenCache"
connectionString="Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=TokenCache;Integrated Security=True;" />
</connectionStrings>
</configuration>
Konfigurowanie appsettings.json (alternatywa)
Ustawienia można również przechowywać w appsettings.json pliku, jak pokazano w poniższym przykładzie.
{
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"TenantId": "your-tenant-id",
"ClientId": "your-client-id",
"ClientSecret": "your-client-secret",
"RedirectUri": "https://localhost:44368/",
"PostLogoutRedirectUri": "https://localhost:44368/"
},
"DownstreamApi": {
"MicrosoftGraph": {
"BaseUrl": "https://graph.microsoft.com/v1.0",
"Scopes": "user.read"
},
"TodoListService": {
"BaseUrl": "https://localhost:44351",
"Scopes": "api://todo-api-client-id/.default"
}
}
}
Konfigurowanie klasy uruchamiania
Zarejestruj oprogramowanie pośredniczące uwierzytelniania, pozyskiwanie tokenów i usługi podrzędnego interfejsu API w klasie startowej.
Konfigurowanie App_Start/Startup.Auth.cs
Poniższy kod przedstawia pełną konfigurację z Microsoft. Identity.Web.OWIN:
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Configuration;
using Microsoft.Identity.Web;
using Microsoft.Identity.Web.OWIN;
using Microsoft.Identity.Web.TokenCacheProviders.Distributed;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.Cookies;
using Microsoft.Owin.Security.OpenIdConnect;
using Owin;
using System;
using System.Configuration;
using System.Web;
namespace MyMvcApp
{
public partial class Startup
{
public void ConfigureAuth(IAppBuilder app)
{
// Set default authentication type
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
// Configure cookie authentication
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
CookieName = "MyApp.Auth",
ExpireTimeSpan = TimeSpan.FromHours(1),
SlidingExpiration = true
});
// Configure OpenID Connect authentication
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
ClientId = ConfigurationManager.AppSettings["AzureAd:ClientId"],
Authority = $"https://login.microsoftonline.com/{ConfigurationManager.AppSettings["AzureAd:TenantId"]}",
RedirectUri = ConfigurationManager.AppSettings["AzureAd:RedirectUri"],
PostLogoutRedirectUri = ConfigurationManager.AppSettings["AzureAd:PostLogoutRedirectUri"],
Scope = "openid profile email offline_access",
ResponseType = "code id_token",
TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
NameClaimType = "preferred_username"
},
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthenticationFailed = context =>
{
context.HandleResponse();
context.Response.Redirect("/Error?message=" + context.Exception.Message);
return Task.FromResult(0);
}
}
});
// Configure Microsoft Identity Web services
var services = CreateOwinServiceCollection();
// Add token acquisition
services.AddTokenAcquisition();
// Add Microsoft Graph support
services.AddMicrosoftGraph();
// Add downstream API support
services.AddDownstreamApi("MicrosoftGraph", services.BuildServiceProvider()
.GetRequiredService<IConfiguration>().GetSection("DownstreamApi:MicrosoftGraph"));
services.AddDownstreamApi("TodoListService", services.BuildServiceProvider()
.GetRequiredService<IConfiguration>().GetSection("DownstreamApi:TodoListService"));
// Configure token cache (choose one option)
ConfigureTokenCache(services);
// Build service provider
var serviceProvider = services.BuildServiceProvider();
// Create and register token acquirer factory
var tokenAcquirerFactory = TokenAcquirerFactory.GetDefaultInstance();
tokenAcquirerFactory.Build(serviceProvider);
// Add OWIN token acquisition middleware
app.Use<OwinTokenAcquisitionMiddleware>(tokenAcquirerFactory);
}
private IServiceCollection CreateOwinServiceCollection()
{
var services = new ServiceCollection();
// Add configuration from appsettings.json and/or Web.config
IConfiguration configuration = new ConfigurationBuilder()
.AddJsonFile("appsettings.json", optional: true)
.AddInMemoryCollection(new Dictionary<string, string>
{
["AzureAd:Instance"] = ConfigurationManager.AppSettings["AzureAd:Instance"],
["AzureAd:TenantId"] = ConfigurationManager.AppSettings["AzureAd:TenantId"],
["AzureAd:ClientId"] = ConfigurationManager.AppSettings["AzureAd:ClientId"],
["AzureAd:ClientSecret"] = ConfigurationManager.AppSettings["AzureAd:ClientSecret"],
["DownstreamApi:MicrosoftGraph:BaseUrl"] = ConfigurationManager.AppSettings["DownstreamApi:MicrosoftGraph:BaseUrl"],
["DownstreamApi:MicrosoftGraph:Scopes"] = ConfigurationManager.AppSettings["DownstreamApi:MicrosoftGraph:Scopes"],
})
.Build();
services.AddSingleton(configuration);
return services;
}
private void ConfigureTokenCache(IServiceCollection services)
{
// Option 1: In-memory cache (development)
services.AddDistributedTokenCaches(cacheServices =>
{
cacheServices.AddDistributedMemoryCache();
});
// Option 2: SQL Server cache (production)
/*
services.AddDistributedTokenCaches(cacheServices =>
{
cacheServices.AddDistributedSqlServerCache(options =>
{
options.ConnectionString = ConfigurationManager.ConnectionStrings["TokenCache"].ConnectionString;
options.SchemaName = "dbo";
options.TableName = "TokenCache";
options.DefaultSlidingExpiration = TimeSpan.FromMinutes(90);
});
});
*/
// Option 3: Redis cache (production, high-scale)
/*
services.AddDistributedTokenCaches(cacheServices =>
{
cacheServices.AddStackExchangeRedisCache(options =>
{
options.Configuration = ConfigurationManager.AppSettings["Redis:ConnectionString"];
options.InstanceName = "MyMvcApp_";
});
});
*/
}
}
}
Integrowanie kontrolerów
Uzyskaj dostęp do Microsoft Graph i podrzędnych interfejsów API z kontrolerów przy użyciu metod rozszerzeń udostępnianych przez pakiet.
Integrowanie kontrolerów MVC
W poniższym przykładzie pokazano, jak używać metod rozszerzenia kontrolera w celu uzyskania dostępu do Microsoft Graph:
using Microsoft.Identity.Web;
using Microsoft.Identity.Web.OWIN;
using Microsoft.Graph;
using System.Threading.Tasks;
using System.Web.Mvc;
namespace MyMvcApp.Controllers
{
[Authorize]
public class HomeController : Controller
{
// GET: Home/Index
public async Task<ActionResult> Index()
{
try
{
// Access Microsoft Graph using extension method
var graphClient = this.GetGraphServiceClient();
var user = await graphClient.Me.GetAsync();
ViewBag.UserName = user.DisplayName;
ViewBag.Email = user.Mail ?? user.UserPrincipalName;
ViewBag.JobTitle = user.JobTitle;
return View();
}
catch (MsalUiRequiredException)
{
// Incremental consent required
return new ChallengeResult();
}
catch (Exception ex)
{
return View("Error", new ErrorViewModel { Message = ex.Message });
}
}
// GET: Home/Profile
public async Task<ActionResult> Profile()
{
var graphClient = this.GetGraphServiceClient();
// Get user profile
var user = await graphClient.Me
.GetAsync(requestConfig => requestConfig.QueryParameters.Select = new[] { "displayName", "mail", "jobTitle", "department" });
return View(user);
}
// GET: Home/Photo
public async Task<ActionResult> Photo()
{
var graphClient = this.GetGraphServiceClient();
try
{
// Get user photo
var photoStream = await graphClient.Me.Photo.Content.GetAsync();
return File(photoStream, "image/jpeg");
}
catch (ServiceException ex) when (ex.StatusCode == System.Net.HttpStatusCode.NotFound)
{
return File(Server.MapPath("~/Content/images/default-user.png"), "image/png");
}
}
}
}
Integrowanie kontrolerów internetowego interfejsu API
W poniższym przykładzie pokazano, jak używać metod rozszerzenia ApiController do wywoływania podrzędnego interfejsu API:
using Microsoft.Identity.Web;
using Microsoft.Identity.Web.OWIN;
using Microsoft.Identity.Abstractions;
using System.Threading.Tasks;
using System.Web.Http;
namespace MyWebApi.Controllers
{
[Authorize]
[RoutePrefix("api/todos")]
public class TodoController : ApiController
{
// GET: api/todos
[HttpGet]
[Route("")]
public async Task<IHttpActionResult> GetTodos()
{
try
{
// Call downstream API using extension method
var downstreamApi = this.GetDownstreamApi();
var todos = await downstreamApi.GetForUserAsync<List<TodoItem>>(
"TodoListService",
options =>
{
options.RelativePath = "api/todolist";
});
return Ok(todos);
}
catch (MsalUiRequiredException)
{
return Unauthorized();
}
catch (HttpRequestException ex)
{
return InternalServerError(ex);
}
}
// POST: api/todos
[HttpPost]
[Route("")]
public async Task<IHttpActionResult> CreateTodo([FromBody] TodoItem todo)
{
var downstreamApi = this.GetDownstreamApi();
var createdTodo = await downstreamApi.PostForUserAsync<TodoItem, TodoItem>(
"TodoListService",
todo,
options =>
{
options.RelativePath = "api/todolist";
});
return Created($"api/todos/{createdTodo.Id}", createdTodo);
}
}
}
Wywoływanie programu Microsoft Graph
Użyj GraphServiceClient do interakcji z danymi Microsoft Graph z kontrolerów.
Konfigurowanie klienta Microsoft Graph
Klient Microsoft Graph jest już skonfigurowany w Startup.Auth.cs z następującym wywołaniem:
services.AddMicrosoftGraph();
Używanie elementu GraphServiceClient w kontrolerach
W poniższym przykładzie pokazano typowe operacje Microsoft Graph w kontrolerze MVC.
[Authorize]
public class GraphController : Controller
{
public async Task<ActionResult> MyProfile()
{
var graphClient = this.GetGraphServiceClient();
var user = await graphClient.Me.GetAsync();
return View(user);
}
public async Task<ActionResult> MyManager()
{
var graphClient = this.GetGraphServiceClient();
var manager = await graphClient.Me.Manager.GetAsync();
return View(manager);
}
public async Task<ActionResult> MyDirectReports()
{
var graphClient = this.GetGraphServiceClient();
var directReports = await graphClient.Me.DirectReports.GetAsync();
return View(directReports.Value);
}
public async Task<ActionResult> SendEmail([FromBody] EmailMessage message)
{
var graphClient = this.GetGraphServiceClient();
var email = new Message
{
Subject = message.Subject,
Body = new ItemBody
{
ContentType = BodyType.Text,
Content = message.Body
},
ToRecipients = new[]
{
new Recipient
{
EmailAddress = new EmailAddress
{
Address = message.To
}
}
}
};
await graphClient.Me.SendMail.PostAsync(new SendMailPostRequestBody
{
Message = email
});
return RedirectToAction("Index");
}
}
Wywoływanie interfejsów API niższego poziomu
Zarejestruj i wywołaj podrzędne interfejsy API przy użyciu automatycznego uzyskiwania tokenu z kontrolerów.
Konfigurowanie podrzędnego interfejsu API
Użyj następującego kodu, aby zarejestrować podrzędny interfejs API w Startup.Auth.cs.
services.AddDownstreamApi("TodoListService", configuration.GetSection("DownstreamApi:TodoListService"));
Dodaj odpowiednie ustawienia w pliku Web.config:
<add key="DownstreamApi:TodoListService:BaseUrl" value="https://localhost:44351" />
<add key="DownstreamApi:TodoListService:Scopes" value="api://todo-api-client-id/.default" />
Używanie interfejsu IDownstreamApi w kontrolerach
W poniższym przykładzie pokazano, jak wykonywać operacje CRUD względem podrzędnego interfejsu API z kontrolera MVC.
[Authorize]
public class TodoController : Controller
{
// GET all todos
public async Task<ActionResult> Index()
{
var downstreamApi = this.GetDownstreamApi();
var todos = await downstreamApi.GetForUserAsync<List<TodoItem>>(
"TodoListService",
options =>
{
options.RelativePath = "api/todolist";
});
return View(todos);
}
// GET specific todo
public async Task<ActionResult> Details(int id)
{
var downstreamApi = this.GetDownstreamApi();
var todo = await downstreamApi.GetForUserAsync<TodoItem>(
"TodoListService",
options =>
{
options.RelativePath = $"api/todolist/{id}";
});
return View(todo);
}
// POST new todo
[HttpPost]
public async Task<ActionResult> Create(TodoItem todo)
{
var downstreamApi = this.GetDownstreamApi();
var createdTodo = await downstreamApi.PostForUserAsync<TodoItem, TodoItem>(
"TodoListService",
todo,
options =>
{
options.RelativePath = "api/todolist";
});
return RedirectToAction("Index");
}
// PUT update todo
[HttpPost]
public async Task<ActionResult> Edit(int id, TodoItem todo)
{
var downstreamApi = this.GetDownstreamApi();
await downstreamApi.CallApiForUserAsync(
"TodoListService",
options =>
{
options.HttpMethod = HttpMethod.Put;
options.RelativePath = $"api/todolist/{id}";
options.RequestBody = todo;
});
return RedirectToAction("Index");
}
// DELETE todo
[HttpPost]
public async Task<ActionResult> Delete(int id)
{
var downstreamApi = this.GetDownstreamApi();
await downstreamApi.CallApiForUserAsync(
"TodoListService",
options =>
{
options.HttpMethod = HttpMethod.Delete;
options.RelativePath = $"api/todolist/{id}";
});
return RedirectToAction("Index");
}
}
Eksplorowanie przykładowych aplikacji
Skorzystaj z poniższych przykładów, aby zobaczyć Microsoft Identity.Web.OWIN w działaniu.
Przejrzyj oficjalne przykłady Microsoft
W poniższej tabeli wymieniono oficjalne przykłady, które pokazują integrację Microsoft.Identity.Web.OWIN.
| Przykład | Opis |
|---|---|
| ms-identity-aspnet-webapp-openidconnect | Aplikacja ASP.NET MVC z Microsoft.Identity.Web.OWIN |
| Pliki kluczowe |
App_Start/Startup.Auth.cs, Controllers/HomeController.cs |
Sklonuj i uruchom przykład za pomocą następujących poleceń:
git clone https://github.com/Azure-Samples/ms-identity-aspnet-webapp-openidconnect
cd ms-identity-aspnet-webapp-openidconnect
# Update Web.config with your Microsoft Entra app registration
# Run in Visual Studio
Postępuj zgodnie z najlepszymi rozwiązaniami
Zastosuj te zalecane wzorce i unikaj typowych błędów podczas tworzenia przy użyciu Microsoft. Identity.Web.OWIN.
Stosowanie zalecanych wzorców
1. Użyj rozproszonej pamięci podręcznej w środowisku produkcyjnym:
// Production
services.AddDistributedTokenCaches(cacheServices =>
{
cacheServices.AddDistributedSqlServerCache(options =>
{
options.ConnectionString = ConfigurationManager.ConnectionStrings["TokenCache"].ConnectionString;
options.SchemaName = "dbo";
options.TableName = "TokenCache";
options.DefaultSlidingExpiration = TimeSpan.FromMinutes(90);
});
});
2. Obsługa przyrostowej zgody w sposób bezproblemowy:
try
{
var graphClient = this.GetGraphServiceClient();
var user = await graphClient.Me.GetAsync();
}
catch (MsalUiRequiredException)
{
// User needs to consent to additional scopes
return new ChallengeResult();
}
3. Użyj identyfikatorów korelacji do rozwiązywania problemów:
var downstreamApi = this.GetDownstreamApi();
var correlationId = Guid.NewGuid();
var result = await downstreamApi.GetForUserAsync<Todo>(
"TodoListService",
options =>
{
options.RelativePath = $"api/todolist/{id}";
options.TokenAcquisitionOptions = new TokenAcquisitionOptions
{
CorrelationId = correlationId
};
});
4. Zaimplementuj właściwą obsługę błędów:
try
{
// Call API
}
catch (MsalUiRequiredException)
{
return new ChallengeResult();
}
catch (HttpRequestException ex)
{
logger.Error($"API call failed: {ex.Message}");
return View("Error");
}
Unikaj typowych błędów
1. Nie używaj pamięci podręcznej w pamięci RAM dla farm serwerów:
// Wrong for load-balanced scenarios
services.AddDistributedTokenCaches(cacheServices =>
{
cacheServices.AddDistributedMemoryCache();
});
// Correct
services.AddDistributedTokenCaches(cacheServices =>
{
cacheServices.AddDistributedSqlServerCache(/* ... */);
});
2. Nie wprowadzaj konfiguracji na stałe w kodzie:
// Wrong
ClientId = "your-client-id-here"
// Correct
ClientId = ConfigurationManager.AppSettings["AzureAd:ClientId"]
3. Nie ignoruj wygaśnięcia tokenu:
// Microsoft.Identity.Web.OWIN handles this automatically
// No manual token refresh needed!
Rozwiązywanie typowych problemów
Zapoznaj się z poniższymi rozwiązaniami typowych problemów występujących podczas instalacji i środowiska uruchomieniowego.
Rozwiązywanie typowych problemów
Problem 1: "Nie można odnaleźć elementu IAuthorizationHeaderProvider"
Rozwiązanie: Upewnij się, że OwinTokenAcquirerFactory jest zarejestrowany w programie Startup.Auth.cs:
var tokenAcquirerFactory = TokenAcquirerFactory.GetDefaultInstance();
tokenAcquirerFactory.Build(serviceProvider);
app.Use<OwinTokenAcquisitionMiddleware>(tokenAcquirerFactory);
Problem 2: "Nie można odnaleźć elementu GraphServiceClient"
Rozwiązanie: Dodaj AddMicrosoftGraph() w Startup.Auth.cs:
services.AddMicrosoftGraph();
Problem 3. Pamięć podręczna tokenu nie jest utrwalana
Rozwiązanie: Sprawdź konfigurację rozproszonej pamięci podręcznej:
services.AddDistributedTokenCaches(cacheServices =>
{
cacheServices.AddDistributedSqlServerCache(options =>
{
// Ensure connection string is correct
options.ConnectionString = ConfigurationManager.ConnectionStrings["TokenCache"].ConnectionString;
});
});
Eksplorowanie powiązanej zawartości
Dowiedz się więcej o powiązanych funkcjach i scenariuszach.