![](/img/trans.png)
[英]Azure AD Authentication with Custom Authorization in ASP.Net Core 6
[英]Asp Core: Azure Ad Auth + custom JWT + custom Identity store
使用ASP.NET Core 2.0,我嘗試實現以下目標:
所有這些部分都有有效的示例,但是在嘗試將它們組合時,我偶然發現了一些問題。
Web Api + Azure Ad Auth示例使用JWT令牌進行身份驗證,但沒有用於驗證或創建令牌的邏輯。 它也沒有登錄/注銷的邏輯,但這似乎是合理的,只是Api。
這是Web Api示例代碼的快速提醒:
AzureAdAuthenticationBuilderExtensions.cs
using System;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
namespace Microsoft.AspNetCore.Authentication
{
public static class AzureAdServiceCollectionExtensions
{
public static AuthenticationBuilder AddAzureAdBearer(this AuthenticationBuilder builder)
=> builder.AddAzureAdBearer(_ => { });
public static AuthenticationBuilder AddAzureAdBearer(this AuthenticationBuilder builder, Action<AzureAdOptions> configureOptions)
{
builder.Services.Configure(configureOptions);
builder.Services.AddSingleton<IConfigureOptions<JwtBearerOptions>, ConfigureAzureOptions>();
builder.AddJwtBearer();
return builder;
}
private class ConfigureAzureOptions: IConfigureNamedOptions<JwtBearerOptions>
{
private readonly AzureAdOptions _azureOptions;
public ConfigureAzureOptions(IOptions<AzureAdOptions> azureOptions)
{
_azureOptions = azureOptions.Value;
}
public void Configure(string name, JwtBearerOptions options)
{
options.Audience = _azureOptions.ClientId;
options.Authority = $"{_azureOptions.Instance}{_azureOptions.TenantId}";
}
public void Configure(JwtBearerOptions options)
{
Configure(Options.DefaultName, options);
}
}
}
}
Startup.cs的摘錄
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication(sharedOptions =>
{
sharedOptions.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddAzureAdBearer(options => Configuration.Bind("AzureAd", options));
services.AddMvc();
}
另一方面,Web應用程序+ Azure廣告示例將OpenId與cookie結合使用,並且具有登錄/注銷邏輯:
AzureAdAuthenticationBuilderExtensions.cs
using System;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
namespace Microsoft.AspNetCore.Authentication
{
public static class AzureAdAuthenticationBuilderExtensions
{
public static AuthenticationBuilder AddAzureAd(this AuthenticationBuilder builder)
=> builder.AddAzureAd(_ => { });
public static AuthenticationBuilder AddAzureAd(this AuthenticationBuilder builder, Action<AzureAdOptions> configureOptions)
{
builder.Services.Configure(configureOptions);
builder.Services.AddSingleton<IConfigureOptions<OpenIdConnectOptions>, ConfigureAzureOptions>();
builder.AddOpenIdConnect();
return builder;
}
private class ConfigureAzureOptions : IConfigureNamedOptions<OpenIdConnectOptions>
{
private readonly AzureAdOptions _azureOptions;
public ConfigureAzureOptions(IOptions<AzureAdOptions> azureOptions)
{
_azureOptions = azureOptions.Value;
}
public void Configure(string name, OpenIdConnectOptions options)
{
options.ClientId = _azureOptions.ClientId;
options.Authority = $"{_azureOptions.Instance}{_azureOptions.TenantId}";
options.UseTokenLifetime = true;
options.CallbackPath = _azureOptions.CallbackPath;
options.RequireHttpsMetadata = false;
}
public void Configure(OpenIdConnectOptions options)
{
Configure(Options.DefaultName, options);
}
}
}
}
Startup.cs的摘錄
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication(sharedOptions =>
{
sharedOptions.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
sharedOptions.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddAzureAd(options => Configuration.Bind("AzureAd", options))
.AddCookie();
services.AddMvc(options =>
{
var policy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
options.Filters.Add(new AuthorizeFilter(policy));
})
.AddRazorPagesOptions(options =>
{
options.Conventions.AllowAnonymousToFolder("/Account");
});
}
AccountController.cs
public class AccountController : Controller
{
[HttpGet]
public IActionResult SignIn()
{
var redirectUrl = Url.Page("/Index");
return Challenge(
new AuthenticationProperties { RedirectUri = redirectUrl },
OpenIdConnectDefaults.AuthenticationScheme
);
}
[HttpGet]
public IActionResult SignOut()
{
var callbackUrl = Url.Page("/Account/SignedOut", pageHandler: null, values: null, protocol: Request.Scheme);
return SignOut(
new AuthenticationProperties { RedirectUri = callbackUrl },
CookieAuthenticationDefaults.AuthenticationScheme, OpenIdConnectDefaults.AuthenticationScheme
);
}
}
我以某種方式合並了這兩個變體,但顯然不起作用。 我在登錄方法JwtBearerDefaults
當然替換為CookieAuthenticationDefault
。
Startup.cs的摘錄
public void ConfigureServices(IServiceCollection services)
{
services.AddEntityFrameworkSqlServer().AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
services.AddAuthentication(sharedOptions =>
{
sharedOptions.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
sharedOptions.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddAzureAd(options => Configuration.Bind("AzureAd", options))
.AddJwtBearer(options =>
{
options.IncludeErrorDetails = true;
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = "localhost",
ValidAudience = "localhost",
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("test"))
};
});
services.AddMvc(options =>
{
var policy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
options.Filters.Add(new AuthorizeFilter(policy));
})
.AddRazorPagesOptions(options =>
{
options.Conventions.AllowAnonymousToFolder("/Account");
});
}
我不完全了解不同的身份驗證如何鏈接或相互依賴。 我了解,OpenId在內部使用某種JWT,仍然存在以下問題:
如果您能為我提供一些指導,那就太好了,而無需完整的工作示例(即使這很好)
如果要在ASP.NET Core Web應用程序中結合使用Cookies和Bearer授權,則可以遵循以下代碼段:
services.AddAuthentication(sharedOptions =>
{
sharedOptions.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
sharedOptions.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddJwtBearer(jwtOptions=> {
jwtOptions.IncludeErrorDetails = true;
jwtOptions.Authority = "{Authority}";
jwtOptions.Audience = "{Audience}";
jwtOptions.TokenValidationParameters = new TokenValidationParameters()
{
ValidIssuer = "{ValidIssuer}",
ValidAudience = "{ValidAudience}"
};
jwtOptions.Events = new JwtBearerEvents()
{
OnAuthenticationFailed = context => {
//TODO:
return Task.FromResult(0);
},
OnTokenValidated = context => {
//At this point, the security token has been validated successfully and a ClaimsIdentity has been created
var claimsIdentity = (ClaimsIdentity)context.Principal.Identity;
//add your custom claims here
claimsIdentity.AddClaim(new Claim("test", "helloworld!!!"));
return Task.FromResult(0);
}
};
})
.AddAzureAd(options => Configuration.Bind("AzureAd", options))
.AddCookie();
我注意到您添加了一個全局AuthorizeFilter
,這時您需要確保匿名動作需要使用AllowAnonymous
屬性進行修飾。
services.AddMvc(options =>
{
var policy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.AddAuthenticationSchemes(CookieAuthenticationDefaults.AuthenticationScheme, JwtBearerDefaults.AuthenticationScheme)
.Build();
options.Filters.Add(new AuthorizeFilter(policy));
})
或者,您可以使用Authorize
屬性裝飾控制器動作,如下所示:
[Authorize(AuthenticationSchemes = "Cookies,Bearer")]
public IActionResult UserInfo()
{
return Json(User.Claims.Select(c => new { key = c.Type, value = c.Value }));
}
對於OpenID Connect中間件,您可以修改AzureAdAuthenticationBuilderExtensions.cs
文件下的Configure(string name, OpenIdConnectOptions options)
方法,以添加自定義聲明(例如,角色等),如下所示:
public void Configure(string name, OpenIdConnectOptions options)
{
options.ClientId = _azureOptions.ClientId;
options.Authority = $"{_azureOptions.Instance}{_azureOptions.TenantId}";
options.UseTokenLifetime = true;
options.CallbackPath = _azureOptions.CallbackPath;
options.RequireHttpsMetadata = false;
//the new code
options.Events = new OpenIdConnectEvents
{
OnTokenValidated = context =>
{
var claimsIdentity = (ClaimsIdentity)context.Principal.Identity;
//add your custom claims here
claimsIdentity.AddClaim(new Claim("test", "helloworld!!!"));
return Task.FromResult(0);
}
};
}
總而言之,在配置后,您可以結合使用Cookies和Bearer身份驗證,並且在驗證令牌之后,您可以檢索用戶標識符並從數據庫中獲取其他用戶信息,然后將它們附加到ClaimsIdentity
。
對於桌面客戶端,您可以利用ADAL (對於AD應用程序v1.0)或MSAL (對於AD應用程序v2.0)登錄並檢索access_token
或id_token
,然后將其用作承載令牌來訪問您的Web應用程序。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.