如何使用依赖注入在ASP.NET Core 2中使用OIDC登录到Azure AD后添加自己的声明?
在ASP.NET的Core 2登录到Azure的AD是相当容易的,在ConfigureServices(IServiceCollection服务)只需添加以下如何使用依赖注入在ASP.NET Core 2中使用OIDC登录到Azure AD后添加自己的声明?
// Azure AD login
services.AddAuthentication(a =>
{
a.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
a.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
a.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
})
.AddCookie(o => o.LoginPath = new PathString("/Account/SignIn"))
.AddOpenIdConnect(o =>
{
o.ClientId = Configuration["Authentication:AzureAd:ClientId"];
o.ClientSecret = Configuration["Authentication:AzureAd:ClientSecret"];
o.Authority = Configuration["Authentication:AzureAd:AADInstance"] +
Configuration["Authentication:AzureAd:TenantId"];
o.CallbackPath = Configuration["Authentication:AzureAd:CallbackPath"];
o.ResponseType = OpenIdConnectResponseType.CodeIdToken;
o.Events = new OpenIdConnectEvents
{
OnRemoteFailure = RemoteFailure,
OnTokenValidated = TokenValidated
};
});
,一切工作正常。然后,我可以在TokenValidated添加索赔和工作正常藏汉:
private Task TokenValidated(TokenValidatedContext context)
{
var claims = new List<Claim>();
var claim = new Claim(ClaimTypes.Role, "Test", ClaimValueTypes.String, "Issuername")
context.Principal.AddIdentity(new ClaimsIdentity(claims));
return Task.FromResult(0);
}
然而,这是从来没有想象中的那么容易。我想要的声明依赖于对服务的外部呼叫,并且地址存储在配置中。
在ConfigureServices中,我还为依赖注入添加了各种类,并且对控制器正常工作。
services.AddTransient<IRoleClaims, RoleClaims>();
这RoleClaims是一类我想从TokenValidated方法调用,但据我可以看到我不能在这里使用DI。我也不能通过ActivatorUtilities.CreateInstance访问ServiceCollection。
构造函数RoleClaims看起来是这样的:
public RoleClaims(IOptions<EmployeeConfiguration> configuration)
所以,最大的问题: 这是如何工作的?我可以以某种方式在TokenValidated方法中使用依赖注入吗?我是否试图在错误的地方添加自己的主张?
在ASP.NET 2.0的核心,你可以从一个服务包含了使用:
private async Task TokenValidated(TokenValidatedContext context)
{
var widget = ctx.HttpContext.RequestServices.GetRequiredService<Widget>();
...
}
我成功地在多租户场景中对IdentityServer4进行身份验证,我需要在每个请求的基础上注入客户端凭证和其他内容。这就是为什么我也“搞砸了”我的代码与自定义OpenIdConnectEvents
。
OnTokenValidated
func是正确的位置。我们的目标是为TokenValidatedContext.Result
(其设置者不幸protected
)赋值。然而 您可以调用.Success()
方法,其中设置相应的属性是什么可供选择:
Task TokenValidated(TokenValidatedContext context)
{
//[...] gathering claims
var ci = new ClaimsIdentity(context.Scheme.Name, "name", "role");
ci.AddClaims(my_previously_gathered_Claims);
context.Principal = new ClaimsPrincipal(ci);
// .Success() uses
// 1. the principal just set above
// 2. the context properties
// 3. the context scheme
// to create the underlying ticket
context.Success();
}
这应该做的伎俩。
我个人更喜欢.Result
的公开二传手。
找到了一个方法来做到这一点。这可能不太好,但它似乎工作。
如果任何人有更好的方法来做到这一点,如果有一些这是不好的做法,我想听听它。
public class Startup
{
private IServiceCollection _serviceColletion;
public void ConfigureServices(IServiceCollection services)
{
_serviceColletion = services; // Hacky way to access services in other methods :s
// services.AddStuff() down here, including the AzureAD OIDC
}
private async Task TokenValidated(TokenValidatedContext context)
{
IRoleClaims roleClaims; // My class for reading from services/database
// and create claims
// This is the magic DI workaround I was looking for
var scopeFactory = _serviceColletion.BuildServiceProvider()
.GetRequiredService<IServiceScopeFactory>();
using (var scope = scopeFactory.CreateScope())
{
var provider = scope.ServiceProvider;
roleClaims = provider.GetRequiredService<IRoleClaims>();
}
// Getting the ObjectID for the user from AzureAD
var objectId = context.SecurityToken.Claims
.Where(o => o.Type == "oid")
.Select(o => o.Value)
.SingleOrDefault();
var claims = await roleClaims.CreateRoleClaimsForUser(objectId);
context.Principal.AddIdentity(new ClaimsIdentity(claims));
}
// Rest of the methods not shown
}
TBH我不太明白什么.Success()是在这里做。当使用context.Principal.AddIdentity(new ClaimsIdentity(myNewClaims))时,似乎并不需要它。 无论如何,问题不是我想要所有的声明都是以相同的身份,而是如何通过依赖注入来访问它们。 – Terje