使用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广告示例使用带有cookie的OpenId,并且具有登录/注销逻辑:
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
);
}
}
我已经以某种方式合并了两种变体,但显然它并不起作用。我在登录方法中将CookieAuthenticationDefault
替换为JwtBearerDefaults
。
摘录 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,仍然存在以下问题:
如果你能给我一些指导,没有完全可行的例子,那就太好了(尽管这会很棒)
答案 0 :(得分:2)
如果您想在ASP.NET Core Web应用程序中组合Cookie和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中间件,您可以修改Configure(string name, OpenIdConnectOptions options)
文件下的AzureAdAuthenticationBuilderExtensions.cs
方法,以添加您的自定义声明(例如角色等),如下所示:
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);
}
};
}
总之,在配置时,您可以组合Cookie和Bearer身份验证,并且在验证令牌后,您可以检索用户标识符并从数据库中获取其他用户信息,然后将其附加到ClaimsIdentity
。
对于桌面客户端,您可以使用ADAL(适用于AD app v1.0)或MSAL(适用于AD app v2.0)登录并检索access_token
或{ {1}},然后将其用作持票人令牌来访问您的Web应用程序。