Wie konfiguriere ich die Azure AD-Authentifizierung im hybriden ASP.NET Core MVC (Backend) und Vuejs SPA (Frontend)?
P粉242741921
P粉242741921 2024-01-08 10:44:05
0
1
585

Meine Anwendung ist ein Hybridansatz mit ASP.NET Core MVC als Backend. Ich habe verschiedene Controller, die mein Frontend verwendet, um Daten aus der Datenbank abzurufen und API-Aufrufe in MS Graph durchzuführen. Ich verwende die folgende program.cs-Datei, um die Authentifizierung zu initiieren, wenn sich ein Benutzer zum ersten Mal auf der Website anmeldet:

//authentication pipline
builder.Services.AddHttpContextAccessor();
var initialScopes = builder.Configuration.GetValue<string>("DownstreamApi:Scopes")?.Split(' ');
builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
                .AddMicrosoftIdentityWebApp(options =>
                {
                    builder.Configuration.Bind("AzureAd", options);
                    options.Events = new OpenIdConnectEvents
                    {
                        //Tap into this event to add a UserID Claim to a new HttpContext identity
                        OnTokenValidated = context =>
                        {
                            //This query returns the UserID from the DB by sending the email address in the claim from Azure AD
                            string query = "select dbo.A2F_0013_ReturnUserIDForEmail(@Email) as UserID";
                            string connectionString = builder.Configuration.GetValue<string>("ConnectionStrings:DBContext");
                            string signInEmailAddress = context.Principal.FindFirst   Value("preferred_username");

                            using (var connection = new SqlConnection(connectionString))
                            {
                                var queryResult = connection.QueryFirst(query, new { Email = signInEmailAddress });

                                var claims = new List<Claim>
                                {
                                    new Claim("UserID", queryResult.UserID.ToString())
                                };

                                var appIdentity = new ClaimsIdentity(claims);

                                context.Principal.AddIdentity(appIdentity);
                            }

                            return Task.CompletedTask;
                        },
                    };
                }).EnableTokenAcquisitionToCallDownstreamApi(initialScopes)
                        .AddMicrosoftGraph(builder.Configuration.GetSection("DownstreamApi"))
                        .AddInMemoryTokenCaches();

//Add Transient Services
builder.Services.AddTransient<IOneDrive, OneDrive>();

builder.Services.AddControllers(options =>
{
    var policy = new AuthorizationPolicyBuilder()
        .RequireAuthenticatedUser()
        .Build();
    options.Filters.Add(new AuthorizeFilter(policy));
}).AddMicrosoftIdentityUI();

builder.Services.AddRazorPages().AddRazorPagesOptions(options =>
{
    options.Conventions.AllowAnonymousToFolder("/Login");
    options.Conventions.AuthorizeFolder("/");
    options.Conventions.AuthorizeFolder("/files");
}).AddMicrosoftIdentityUI();

// Add the UI support to handle claims challenges
builder.Services.AddServerSideBlazor()
   .AddMicrosoftIdentityConsentHandler();
builder.Services.AddRequiredScopeAuthorization();

Im Azure AD-Portal ist meine Anwendung als Webanwendung registriert. Wenn Benutzer zum ersten Mal auf die Website zugreifen, werden sie daher zu https://login.microsoftonline.com/blahblah weitergeleitet, um den Anmeldevorgang zu starten. Dies wird automatisch von der Azure AD-Identitätsplattform durchgeführt. Sobald die Anmeldung erfolgt ist, werden sie dann zum Localhost (localhost:43862) umgeleitet, wo VueJS Spa geladen wird. Mein Spa verwendet verschiedene Axios-Anfragen an Controller, die Daten abrufen, und Vue-Router zum Laden von Komponenten. Mein Problem besteht jedoch darin, dass sich der Benutzer erneut anmelden muss, weil das Cookie abgelaufen ist oder er sich in einem anderen Tab abgemeldet hat. Die nächste Axios-Anfrage aus einer abgelaufenen Sitzung leitet den Benutzer nicht zum Azure-Anmeldebildschirm weiter, sondern führt zu einem CORS-Fehler. Ich muss also dafür sorgen, dass meine Axios-Anfrage die Seite dazu zwingt, zum Azure AD-Anmeldebildschirm umzuleiten (was wahrscheinlich die schlechteste Idee ist, da die CORS-Richtlinie Fehler verursacht), oder dass sie zurück zu localhost/login umgeleitet wird, was ich tue Ein eigener benutzerdefinierter Anmeldebildschirm mit der Azure AD-Anmeldeschaltfläche sollte sich nicht auf CORS auswirken. Wie kann ich also diese Azure AD-Umleitung zum Azure AD-Login abfangen und durch meine eigene ersetzen?

Ich habe auch versucht, einen 401-Fehlercode zurückzugeben, damit ich das in der Axios-Anfrage überprüfen konnte, aber ohne Erfolg, es passiert nichts. Wenn ich dort einen Haltepunkt setze, trifft er zwar auf diesen Code, ändert aber nichts am Statuscode der Antwort, ich erhalte immer noch eine 302. Mein Code versucht, dem Ereignis Folgendes hinzuzufügen:

OnRedirectToIdentityProvider = context =>
                    {
                        context.Response.StatusCode = 401;
                        return Task.CompletedTask;
                    }

Meine anderen Gedanken sind, dass ich vielleicht die CORS-Richtlinie einrichten sollte, um Weiterleitungen von login.microsoft.com zuzulassen? Oder ist das eine schlechte Praxis?

P粉242741921
P粉242741921

Antworte allen(1)
P粉501007768

我可以回答你的部分问题...首先,对于我们受 Azure AD 保护的 API 应用程序,API 应该做的是验证请求是否在请求标头中包含正确的访问令牌,如果是,给出响应,如果没有,则给出 401 或 403 之类的错误。普通的 API 应用程序不应该有 UI 来让用户登录。无论如何,如果你想在 MVC 项目中公开 API,这是可以的,但是对于API 本身,它不应该有 UI。

让我们看看下面的示例,我有一个 .net 6 Web api 项目,这是我的 program.cs

using Microsoft.Identity.Web;

var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddMicrosoftIdentityWebApiAuthentication(builder.Configuration);
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.Run();

并且需要在appsetting.json中进行配置。

"AzureAd": {
    "Instance": "https://login.microsoftonline.com/",
    "ClientId": "azure_ad_client_id",
    "ClientSecret": "client_secret",
    "Domain": "tenant_id",
    "TenantId": "tenant_id",
    //"Audience": "api://azure_ad_client_id_which exposed_api" // here I used the same azure ad app to expose API, so I can comment this property
  },

这是控制器:

[ApiController]
    [Route("[controller]")]
    [Authorize]
    public class WeatherForecastController : ControllerBase
    {
        [RequiredScope("Tiny.Read")]
        [HttpGet]
        public string Get()
        {
            return "world";
        }
    }

我有一个 Azure AD 应用程序,并且公开了如下 API:

我还为同一个 Azure AD 应用添加了此 API。

那我们来做个测试吧。当我直接调用这个API时,我会得到401错误:

如果我在请求中使用了过期的令牌,我也会收到 401 错误:

但是如果我使用了正确的令牌(转到 https://jwt.io 来解码令牌,我们应该看到它包含正确的范围,对我来说它的 "scp": "Tiny.Read",),我会得到响应:

至此,API部分已经完成。让我们看看客户端 SPA。对于 SPA,您应该集成 MSAL,以便您可以让您的用户通过 Azure AD 登录,并生成用于调用 MS graph API 或您自己的 API 的访问令牌。生成访问令牌的代码应该相同,但您应该为不同的API设置不同的范围。在我的场景中,我的 API 需要一个范围 Tiny.Read,那么我应该在我的客户端应用程序中设置。

这是在react中生成访问令牌的屏幕截图。您需要在代码中设置范围。

现在您已经有了生成访问令牌的方法,您已经知道了 API url。然后你可以发送请求调用api,使用AJAX,使用fetch,或者其他什么,发送http请求就可以了。并且在调用api部分,还需要处理响应。如果响应代码是 401,那么您需要执行一些逻辑,可能会重定向到登录页面。你说你在这里遇到了麻烦,你遇到了 CORS 问题。这部分我无法回答。我认为这取决于您如何重定向到 Azure AD 登录页面。恐怕你可以看看 此示例,了解如何登录用户和调用图形 api。

Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage