在运行时替换Cookie身份验证处理程序选项

时间:2017-12-05 05:29:03

标签: c# asp.net-core .net-core asp.net-core-mvc-2.0

如果我使用本机依赖注入容器向我的ASP.Net核心应用程序添加cookie身份验证,并使用cookie身份验证选项。那么如何在启动后在运行时替换身份验证选项?例如,如果我想在应用程序运行时更改cookie过期。我无法弄清楚如何用它的选项替换身份验证处理程序以影响更改。

启动时代码添加身份验证:

public static IServiceCollection ConfigureOAuth(this IServiceCollection services)
{
    var appSettings = services.BuildServiceProvider().GetService<IOptions<AppSettings>>();

    return services.AddAuthentication(o =>
    {
        o.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
        o.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
    })
    .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, (o) =>
    {
        o.ExpireTimeSpan = TimeSpan.FromHours(appSettings.Value.HostOptions.SessionLifespanHours);
    })
    .Services;
}

代码在运行时替换身份验证:

/// <summary>
/// Replace authentication options with new ones read from configuration. 
/// 1). Remove old services
/// 2.) Reload the configuration 
/// 3.) Add the authentication scheme with options read from the latest configuration
/// </summary>
private static void ReplaceServices(IServiceCollection services, IHostingEnvironment env)
{
    ClearServices(services);

    services.Configure<AppSettings>(StartupConfiguration.BuildConfigurationRoot(env).GetSection("App"));

    var provider = services.BuildServiceProvider();
    var appSettings = provider.GetService<IOptions<AppSettings>>();

    services.TryAddEnumerable(ServiceDescriptor.Singleton<IPostConfigureOptions<CookieAuthenticationOptions>, PostConfigureCookieAuthenticationOptions>());
    services.AddScheme<CookieAuthenticationOptions, CookieAuthenticationHandler>(CookieAuthenticationDefaults.AuthenticationScheme, (o) =>
    {
        o.ExpireTimeSpan = TimeSpan.FromHours(appSettings.Value.HostOptions.SessionLifespanHours);
    });
}

/// <summary>
/// Clear stale dependencies: application settings configured from appsettings.json, 
/// authentication options and cookie authentication handler and options
/// </summary>
private static void ClearServices(IServiceCollection services)
{
    var staleTypes = new List<Type>
    {
        typeof(IConfigureOptions<AppSettings>),
        typeof(IConfigureOptions<AuthenticationOptions>),
        typeof(IPostConfigureOptions<CookieAuthenticationOptions>),
        typeof(IConfigureOptions<CookieAuthenticationOptions>),
        typeof(CookieAuthenticationHandler)
    };

    foreach (var staleType in staleTypes)
    {
        var staleService = services.FirstOrDefault(s => s.ServiceType.Equals(staleType));
        services.Remove(staleService);
    }
}

1 个答案:

答案 0 :(得分:1)

Asp.net核心本机配置重新加载可能有点不稳定。如果服务依赖于在运行时更改的应用程序设置,则不必在启动时将这些设置作为IOptions注入。另一种方法是编写设置提供程序,在从文件系统观察程序收到事件通知时重新加载设置的缓存副本。这种方法消除了将配置作为DI服务的需要,您不再需要依赖重新加载令牌。流程如下:

  1. 创建一个配置提供程序服务,该服务封装了应用程序设置的读取和存储。在init上,它读取appsettings.json并缓存IConfigurationRoot的实例
  2. 创建另一项服务以封装观看文件系统以更改应用程序设置。如果更改,请使用带有FileChangeEvent的简单发布/订阅模式通知配置提供程序。配置提供程序可以更新配置根,然后在刷新配置后触发ConfigChangeEvent。
  3. 他们依赖实时配置的任何服务(例如身份验证选项)都可以订阅ConfigChangeEvent并根据配置根目录所需的部分更新其设置。
  4. 这里的关键是创建一个认证处理程序可以成功使用并始终具有实时值的选项服务。为此,您需要实现IOptionsMonitor。

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddAuthentication(o =>
        {
            o.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
            o.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
        })
        .AddSingleton<IOptionsMonitor<CookieAuthenticationOptions>, CookieAuthenticationConfigurator>()
        .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme);
    }
    

    IOptionsMonitor实现:

    {
        internal class CookieAuthenticationConfigurator : IOptionsMonitor<CookieAuthenticationOptions>
        {
            private readonly FileConfigurationBuilder ConfigProvider;
            private readonly IHostingEnvironment Environment;
            private readonly IDataProtectionProvider DataProtectionProvider;
            private readonly IMessageHub Hub;
    
            public CookieAuthenticationConfigurator(FileConfigurationBuilder configProvider, IDataProtectionProvider dataProtectionProvider, IMessageHub hub, IHostingEnvironment environment)
            {
                ConfigProvider = configProvider;
                Environment = environment;
                DataProtectionProvider = dataProtectionProvider;
                Hub = hub;
                Initialize();
            }
    
            private void Initialize()
            {
                Hub.Subscribe<ConfigurationChangeEvent>(_ =>
                {
                    Build();
                });
    
                Build();
            }
    
            private void Build()
            {
                var hostOptions = ConfigProvider.Get<HostOptions>("HostOptions");
                options = new CookieAuthenticationOptions
                {
                    ExpireTimeSpan = TimeSpan.FromHours(hostOptions.SessionLifespanHours)
                };
            }
    
            private CookieAuthenticationOptions options;
    
            public CookieAuthenticationOptions CurrentValue => options;
    
            public CookieAuthenticationOptions Get(string name)
            {
                PostConfigureCookieAuthenticationOptions op = new PostConfigureCookieAuthenticationOptions(DataProtectionProvider);
                op.PostConfigure(name, options);
                return options;
            }
    
            public IDisposable OnChange(Action<CookieAuthenticationOptions, string> listener)
            {
                throw new NotImplementedException();
            }
        }
    }