C# aspnetcore2.0使用带有AzureAd身份验证的服务
我将要将ASP.NET核心MVC web应用程序从1.1迁移到2.0。该应用程序使用AzureAd进行身份管理 在1.1中,我在Startup.cs(在C# aspnetcore2.0使用带有AzureAd身份验证的服务,c#,asp.net-core,entity-framework-core,asp.net-core-2.0,C#,Asp.net Core,Entity Framework Core,Asp.net Core 2.0,我将要将ASP.NET核心MVC web应用程序从1.1迁移到2.0。该应用程序使用AzureAd进行身份管理 在1.1中,我在Startup.cs(在Configure()中)处理了openidconnect事件(如OnTokenReceiver,OnAuthorizationCodeReceived,OnRemoteFailure等),在这里我可以使用依赖注入。我已经注入了很多服务,比如efdb context,并在事件处理程序中使用它们。 升级到2.0后,我必须将整个身份验证迁移到Azur
Configure()
中)处理了openidconnect事件(如OnTokenReceiver
,OnAuthorizationCodeReceived
,OnRemoteFailure
等),在这里我可以使用依赖注入。我已经注入了很多服务,比如efdb context,并在事件处理程序中使用它们。
升级到2.0后,我必须将整个身份验证迁移到AzureAuthenticationBuilderExtensions
的ConfigureAzureOptions
类(实现IConfigureNameOptions
接口),其中(如我所见)无法使用DI
因此,现在只有这一项在Startup的ConfigureServices中:
services.AddAuthentication(sharedOptions =>
{
sharedOptions.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
sharedOptions.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddAzureAd(options => Configuration.Bind("AzureAd", options))
.AddCookie();
我在迁移过程中使用了以下指南:
有人知道如何在openidconnect事件中使用服务吗
更新:我在@Balah的回答的帮助下解决了这个问题。基本上,解决方案是使用通用的.AddOpenIdConnect()
而不是创建名为.AddAzureAd()的扩展名
对答案的一点补充:由于身份验证部分从Configure()
移动到ConfigureServices()
,其中未启用DI且服务尚未注册,因此获取这些服务的方法毕竟是:
var scopeFactory = services
.BuildServiceProvider()
.GetRequiredService<IServiceScopeFactory>();
var scope = scopeFactory.CreateScope();
var provider = scope.ServiceProvider;
var dbContext = provider.GetRequiredService<ApplicationDbContext>();
var graphSdkHelper = provider.GetRequiredService<IGraphSDKHelper>();
var memoryCache = provider.GetRequiredService<IMemoryCache>();
...
var scopeFactory=服务
.BuildServiceProvider()
.GetRequiredService();
var scope=scopeFactory.CreateScope();
var provider=scope.ServiceProvider;
var dbContext=provider.GetRequiredService();
var graphsdkheloper=provider.GetRequiredService();
var memoryCache=provider.GetRequiredService();
...
请记住,您必须将这些服务添加到此代码之上 您会发现您提到的那些事件已被移动到身份验证选项上的(恰当命名的)events
属性中
可以通过HttpContext.RequestServices
属性访问DI容器中注册的任何服务,如下所示:
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddOpenIdConnect(o =>
{
o.Events.OnAuthorizationCodeReceived = async ctx =>
{
var db = ctx.HttpContext.RequestServices.GetService<DbContext>();
await ...
};
});
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddOpenIdConnect(o=>
{
o、 Events.OnAuthorizationCodeReceived=异步ctx=>
{
var db=ctx.HttpContext.RequestServices.GetService();
等待。。。
};
});
您可能需要添加services.AddSingleton()因为我也有。但我怀疑如果没有它,上述方法也会起作用
这里有一个很好的例子。我正在使用.net core 3.1,我也遇到了类似的问题。
我认为通过将身份验证逻辑移动到一个单独的处理程序类,可以使它变得更干净,因为我们希望尽可能地保持Startup.cs的紧密性
public class AzureAdOpendIdHandler : IConfigureNamedOptions<OpenIdConnectOptions>
{
public void Configure(string name, OpenIdConnectOptions options)
{
options.ClientId = _azureOptions.ClientId;
options.UseTokenLifetime = true;
// The callback path located in AzureAd settings should match the callback path setup up in Azure portal
options.CallbackPath = _azureOptions.CallbackPath;
options.RequireHttpsMetadata = false;
options.ResponseType = OpenIdConnectResponseType.CodeIdToken;
options.TokenValidationParameters = new TokenValidationParameters
{
// Ensure that User.Identity.Name is set correctly after login
NameClaimType = JwtRegisteredClaimNames.Email,
ValidateIssuer = false,
};
options.Events = new OpenIdConnectEvents
{
OnTokenValidated = async context =>
{
var dbContext = (HighEloDbContext)context.HttpContext.RequestServices.GetService(typeof(HighEloDbContext));
var acc = dbContext.Accounts.First(x => x.EmailAddress == userEmail);
...
},
OnAuthenticationFailed = context =>
{
context.Response.Redirect("/Error");
context.HandleResponse(); // Suppress the exception
return Task.CompletedTask;
},
};
}
public void Configure(OpenIdConnectOptions options)
{
Configure(Options.DefaultName, options);
}
}
公共类AzureAdOpendIdHandler:IConfigureNamedOptions
{
public void配置(字符串名称、OpenIdConnectOptions选项)
{
options.ClientId=\u azureOptions.ClientId;
options.UseTokenLifetime=true;
//AzureAd设置中的回调路径应与Azure portal中设置的回调路径匹配
options.CallbackPath=\u azureOptions.CallbackPath;
options.RequireHttpsMetadata=false;
options.ResponseType=OpenIdConnectResponseType.CodeIdToken;
options.TokenValidationParameters=新的TokenValidationParameters
{
//确保在登录后正确设置User.Identity.Name
NameClaimType=JwtRegisteredClaimNames.Email,
validateisuer=false,
};
options.Events=新的OpenIdConnectEvents
{
OnTokenValidated=异步上下文=>
{
var dbContext=(highelodcontext)context.HttpContext.RequestServices.GetService(typeof(highelodcontext));
var acc=dbContext.Accounts.First(x=>x.EmailAddress==userEmail);
...
},
OnAuthenticationFailed=上下文=>
{
context.Response.Redirect(“/Error”);
context.HandleResponse();//抑制异常
返回Task.CompletedTask;
},
};
}
public void配置(OpenIdConnectOptions选项)
{
配置(Options.DefaultName,Options);
}
}
下面是我的Starup.cs的样子:
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages().AddMvcOptions(options =>
{
var policy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
options.Filters.Add(new AuthorizeFilter(policy));
});
services.AddControllersWithViews().AddRazorRuntimeCompilation();
services.AddAuthentication(AzureADDefaults.AuthenticationScheme)
.AddAzureAD(options => { Configuration.Bind(nameof(AzureAdConfig), options); });
//here comes registration of services, DAL contexts etc.
services.AddSingleton<IConfigureOptions<OpenIdConnectOptions>, AzureAdOpendIdHandler>();
}
public void配置服务(IServiceCollection服务)
{
services.AddRazorPages().addmvcopions(选项=>
{
var policy=new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()文件
.Build();
options.Filters.Add(新的授权过滤器(策略));
});
services.AddControllersWithViews().AddRazorRuntimeCompilation();
services.AddAuthentication(AzureADDefaults.AuthenticationScheme)
.AddAzureAD(options=>{Configuration.Bind(nameof(AzureAdConfig),options);});
//这里是服务注册,等等。
services.AddSingleton();
}
请注意,它可以与.AddAzureAD
一起使用,这似乎不起作用,因为DI在ConfigureServices中不起作用(身份验证已移动),并且我无法在Startup()构造函数中添加服务,因为它们还不存在。我在第一次登录时发送电子邮件,这是在OnTokenReceived()中完成的,但是电子邮件服务还不存在。这很有趣。DI应该在这些事件中工作,因为容器是在调用这些事件之前构建并可用的。介意用一段在1.1中使用的代码更新你的问题吗?(您是对的-Startup()构造函数肯定不起作用)我使用的代码与1.1中的代码几乎相同,但我不得不将它从Configure()移动到ConfigureServices(),在那里没有DI。我能够让DbContext工作,但我的其他服务(Graph SDK助手、电子邮件、历史记录)无法工作…好的,我能够解决这个问题,请参阅t