Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/282.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何在使用依赖项注入在ASP.NET Core 2中使用OIDC登录Azure AD后添加自己的声明?_C#_Dependency Injection_Azure Active Directory_Claims_Asp.net Core 2.0 - Fatal编程技术网

C# 如何在使用依赖项注入在ASP.NET Core 2中使用OIDC登录Azure AD后添加自己的声明?

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

在ASP.NET Core 2中,登录Azure AD相当容易,在ConfigureServices(iSeries收集服务)中,只需添加以下内容

// 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))时不需要它。无论如何,问题不在于我希望所有声明都具有相同的身份,而在于如何通过依赖注入来访问它们。