Catatan
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba masuk atau mengubah direktori.
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba mengubah direktori.
Tambahkan autentikasi modern ke aplikasi ASP.NET MVC dan Web API Anda pada .NET Framework 4.7.2+ dengan menggunakan Microsoft. Paket Identity.Web.OWIN.
Memahami integrasi OWIN
Paket Microsoft.Identity.Web.OWIN membawa kekuatan Microsoft.Identity.Web ke aplikasi ASP.NET MVC dan Web API yang menggunakan middleware OWIN dengan Microsoft Entra ID.
Meninjau manfaat utama
Tabel berikut ini meringkas fitur utama dan manfaat paket.
| Feature | Keuntungan |
|---|---|
| TokenAcquirerFactory | Akuisisi token otomatis dengan penyimpanan sementara |
| Ekstensi Pengontrol | Akses mudah ke GraphServiceClient dan IDownstreamApi |
| Singgahan Token Terdistribusi | Dukungan bawaan untuk SQL Server, Redis, Cosmos DB, PostgreSQL |
| Pembaruan Otomatis Token | Menangani refresh token secara transparan |
| Persetujuan Inkremental | Integrasi alur persetujuan yang mulus |
Meninjau skenario yang didukung
Microsoft. Identity.Web.OWIN mendukung jenis dan skenario aplikasi berikut.
- Aplikasi Web ASP.NET MVC (.NET Framework 4.7.2+)
- API Web ASP.NET (.NET Framework 4.7.2+)
- Aplikasi Hibrid (MVC + WEB API)
- Memanggil Microsoft Graph dari pengontrol
- Memanggil API Hilir dengan autentikasi otomatis
Pasang paketnya
Instal paket Microsoft.Identity.Web.OWIN NuGet dengan metode pilihan Anda.
Jalankan perintah berikut di Konsol Package Manager:
Install-Package Microsoft.Identity.Web.OWIN
Atau, jalankan perintah berikut dengan .NET CLI:
dotnet add package Microsoft.Identity.Web.OWIN
Dependensi secara otomatis disertakan:
- Microsoft.Identity.Web.TokenAcquisition
- Microsoft. Identity.Web.TokenCache
- Microsoft.Owin
- System.Web
Mengonfigurasi aplikasi
Konfigurasikan pengaturan aplikasi Anda untuk terhubung ke Microsoft Entra dan API hilir.
Mengonfigurasi Web.config
Tambahkan pengaturan Microsoft Entra dan API turunan berikut ke file Web.config Anda.
<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>
Mengonfigurasi appsettings.json (alternatif)
Anda juga dapat menyimpan pengaturan dalam file seperti yang appsettings.json ditunjukkan dalam contoh berikut.
{
"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"
}
}
}
Mengatur kelas inisialisasi
Daftarkan middleware autentikasi, akuisisi token, dan layanan API hilir di kelas startup Anda.
Mengonfigurasi App_Start/Startup.Auth.cs
Kode berikut menunjukkan penyiapan lengkap dengan 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_";
});
});
*/
}
}
}
Mengintegrasikan pengontrol
Akses Microsoft Graph dan API hilir dari pengontrol Anda dengan menggunakan metode ekstensi yang disediakan oleh paket.
Mengintegrasikan pengontrol MVC
Contoh berikut menunjukkan cara menggunakan metode ekstensi pengontrol untuk mengakses 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");
}
}
}
}
Mengintegrasikan pengontrol API Web
Contoh berikut menunjukkan cara menggunakan metode ekstensi ApiController untuk memanggil API hilir:
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);
}
}
}
Memanggil Microsoft Graph
Gunakan GraphServiceClient untuk berinteraksi dengan data Microsoft Graph dari pengontrol Anda.
Menyiapkan klien Microsoft Graph
Klien Microsoft Graph sudah dikonfigurasi di Startup.Auth.cs dengan panggilan berikut:
services.AddMicrosoftGraph();
Menggunakan GraphServiceClient di pengontrol
Contoh berikut menunjukkan operasi Microsoft Graph umum dalam pengontrol 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");
}
}
Panggil atau akses API hilir
Daftarkan dan panggil API hilir dengan akuisisi token otomatis dari pengontrol Anda.
Mengonfigurasi API hilir
Daftarkan API hilir dengan Startup.Auth.cs kode berikut:
services.AddDownstreamApi("TodoListService", configuration.GetSection("DownstreamApi:TodoListService"));
Tambahkan pengaturan yang sesuai di Web.config:
<add key="DownstreamApi:TodoListService:BaseUrl" value="https://localhost:44351" />
<add key="DownstreamApi:TodoListService:Scopes" value="api://todo-api-client-id/.default" />
Menggunakan IDownstreamApi di pengontrol
Contoh berikut menunjukkan cara melakukan operasi CRUD terhadap API hilir dari pengontrol 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");
}
}
Menjelajahi aplikasi sampel
Gunakan sampel berikut untuk melihat Microsoft. Identity.Web.OWIN sedang beraksi.
Memeriksa contoh Microsoft resmi
Tabel berikut mencantumkan sampel resmi yang menunjukkan integrasi Microsoft.Identity.Web.OWIN.
| Sampel | Deskripsi |
|---|---|
| ms-identity-aspnet-webapp-openidconnect | Aplikasi ASP.NET MVC dengan Microsoft.Identity.Web.OWIN |
| File Kunci |
App_Start/Startup.Auth.cs, Controllers/HomeController.cs |
Kloning dan jalankan sampel dengan perintah berikut:
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
Ikuti praktik terbaik
Terapkan pola yang direkomendasikan ini dan hindari kesalahan umum saat membangun dengan Microsoft. Identity.Web.OWIN.
Menerapkan pola yang direkomendasikan
1. Gunakan cache terdistribusi dalam produksi:
// 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. Tangani persetujuan inkremental dengan anggun:
try
{
var graphClient = this.GetGraphServiceClient();
var user = await graphClient.Me.GetAsync();
}
catch (MsalUiRequiredException)
{
// User needs to consent to additional scopes
return new ChallengeResult();
}
3. Gunakan ID korelasi untuk pemecahan masalah:
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. Terapkan penanganan kesalahan yang tepat:
try
{
// Call API
}
catch (MsalUiRequiredException)
{
return new ChallengeResult();
}
catch (HttpRequestException ex)
{
logger.Error($"API call failed: {ex.Message}");
return View("Error");
}
Hindari kesalahan umum
1. Jangan gunakan cache dalam memori untuk farm web:
// Wrong for load-balanced scenarios
services.AddDistributedTokenCaches(cacheServices =>
{
cacheServices.AddDistributedMemoryCache();
});
// Correct
services.AddDistributedTokenCaches(cacheServices =>
{
cacheServices.AddDistributedSqlServerCache(/* ... */);
});
2. Jangan meng-hardcode konfigurasi:
// Wrong
ClientId = "your-client-id-here"
// Correct
ClientId = ConfigurationManager.AppSettings["AzureAd:ClientId"]
3. Jangan abaikan kedaluwarsa token:
// Microsoft.Identity.Web.OWIN handles this automatically
// No manual token refresh needed!
Pemecahan Masalah Umum
Tinjau solusi berikut untuk masalah umum yang terjadi selama penyiapan dan runtime.
Menyelesaikan masalah umum
Masalah 1: "Tidak dapat menemukan IAuthorizationHeaderProvider"
Solusi: Pastikan OwinTokenAcquirerFactory terdaftar di Startup.Auth.cs:
var tokenAcquirerFactory = TokenAcquirerFactory.GetDefaultInstance();
tokenAcquirerFactory.Build(serviceProvider);
app.Use<OwinTokenAcquisitionMiddleware>(tokenAcquirerFactory);
Masalah 2: "Tidak dapat menemukan GraphServiceClient"
Solusi: Tambahkan AddMicrosoftGraph() di Startup.Auth.cs:
services.AddMicrosoftGraph();
Masalah 3: Cache token tidak bertahan
Solusi: Verifikasi konfigurasi cache terdistribusi:
services.AddDistributedTokenCaches(cacheServices =>
{
cacheServices.AddDistributedSqlServerCache(options =>
{
// Ensure connection string is correct
options.ConnectionString = ConfigurationManager.ConnectionStrings["TokenCache"].ConnectionString;
});
});
Menjelajahi konten terkait
Pelajari selengkapnya tentang fitur dan skenario terkait.