C# 如何在使用依赖项注入在ASP.NET Core 2中使用OIDC登录Azure AD后添加自己的声明?
在ASP.NET Core 2中,登录Azure AD相当容易,在ConfigureServices(iSeries收集服务)中,只需添加以下内容C# 如何在使用依赖项注入在ASP.NET Core 2中使用OIDC登录Azure AD后添加自己的声明?,c#,dependency-injection,azure-active-directory,claims,asp.net-core-2.0,C#,Dependency Injection,Azure Active Directory,Claims,Asp.net Core 2.0,在ASP.NET Core 2中,登录Azure AD相当容易,在ConfigureServices(iSeries收集服务)中,只需添加以下内容 // Azure AD login services.AddAuthentication(a => { a.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme; a.DefaultSignInScheme = CookieAuthenticati
// 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);
}
private Task TokenValidated(TokenValidatedContext上下文)
{
var索赔=新列表();
var索赔=新索赔(ClaimTypes.Role,“Test”,ClaimValueTypes.String,“Issuername”)
背景、主体、附加性(新索赔(索赔));
返回Task.FromResult(0);
}
然而,这从来没有那么容易。我想要的声明依赖于对服务的外部调用,并且地址存储在配置中
在ConfigureServices中,我还添加了各种用于依赖项注入的类,这些类对于控制器来说工作良好
services.AddTransient<IRoleClaims, RoleClaims>();
services.AddTransient();
这个RoleClaims是我想从TokenValidated方法调用的一个类,但就我所见,我不能在这里使用DI。我也无法通过ActivatorUtilities.CreateInstance访问ServiceCollection以获取它
RoleClaims的构造函数如下所示:
public RoleClaims(IOptions<EmployeeConfiguration> configuration)
public RoleClaims(IOptions配置)
所以,最大的问题是:
这是怎么回事?我可以在TokenValidated方法中使用依赖项注入吗?我是否试图在错误的地方添加我自己的声明 在多租户场景中,我成功地根据IdentityServer4进行了身份验证,在该场景中,我需要根据每个请求注入客户端凭据和其他内容。这就是为什么我还用自定义的
OpenIdConnectEvents
将我的代码“弄乱”了
OnTokenValidated
func是正确的位置。目标是为TokenValidatedContext.Result
(其设置程序不幸受到保护
)分配一个值。
但是,您可以调用.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();
}
这应该能奏效
我个人更喜欢公共setter,因为
。Result
找到了一种方法。这可能并不漂亮,但似乎很管用
如果有人有更好的方法来做这件事,当然,如果这是一个坏的做法,我想听听
public class Startup
{
private IServiceCollection _serviceCollection;
public void ConfigureServices(IServiceCollection services)
{
_serviceCollection = 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 = _serviceCollection.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
}
公共类启动
{
私人IServiceCollection_serviceCollection;
public void配置服务(IServiceCollection服务)
{
_serviceCollection=services;//用其他方法访问服务的黑客方法:s
//下面是services.AddStuff(),包括AzureAD OIDC
}
专用异步任务TokenValidated(TokenValidatedContext上下文)
{
IRoleClaims roleClaims;//用于从服务/数据库读取的我的类
//并创建索赔
//这就是我一直在寻找的神奇DI解决方案
var scopeFactory=\u servicecolection.BuildServiceProvider()
.GetRequiredService();
使用(var scope=scopeFactory.CreateScope())
{
var provider=scope.ServiceProvider;
roleClaims=provider.GetRequiredService();
}
//从AzureAD获取用户的ObjectID
var objectId=context.SecurityToken.Claims
其中(o=>o.Type==“oid”)
.选择(o=>o.Value)
.SingleOrDefault();
var索赔=等待roleClaims.CreateRoleClaimsFruser(objectId);
背景、主体、附加性(新索赔(索赔));
}
//其余方法未显示
}
在ASP.NET Core 2.0中,您可以通过以下方式从包含中获取服务:
private async Task TokenValidated(TokenValidatedContext context)
{
var widget = ctx.HttpContext.RequestServices.GetRequiredService<Widget>();
...
}
专用异步任务TokenValidated(TokenValidatedContext上下文)
{
var widget=ctx.HttpContext.RequestServices.GetRequiredService();
...
}
Tbh我不太明白.Success()在这里做什么。至少在使用context.Principal.AddIdentity(newclaimsidentity(myNewClaims))时不需要它。无论如何,问题不在于我希望所有声明都具有相同的身份,而在于如何通过依赖注入来访问它们。