在 MVC 应用程序中的何处使用 WIF 创建自定义标识?

Where to create custom Identity with WIF in a MVC application?

在 WIF 之前,Application_PostAcquireRequestState 是创建自定义身份的好地方,但需要大量框架来确保正确映射您正在执行的每种类型的身份验证。通过自定义身份,我的意思是 class 从身份继承,例如下面的 SomeIdentity,这样我们就可以拥有特定的 属性 我们可能要求所有经过身份验证的用户都拥有。

PostAcquirerequestState 仍然可用,但有许多新方法可以挂接到身份验证。此外,旧方法在支持多种身份验证方法时会变得复杂。

我想知道现在在 WIF 中是否有比下面更好的方法来完成此操作。主要是我想分离出处理映射声明到身份的代码。这个想法是代码对于其他身份验证 types/providers 会有所不同,因为它检索 属性 值的方式可能不是来自 SAML 等声明,而是来自其他地方的其他类型的身份验证方法.我目前正在使用 Kentor.AuthServices 来支持 SAML。虽然根据提供者可能有不同的代码来映​​射这些值,但最终结果将是创建了一个 SomeIdentity 实例并且它是 SomeProperty 并且将设置其他属性。这样应用程序的其余部分总是可以 depend/assume 那些已经被处理。

我的 MVC 项目带有一个 AccountController,它有一个 ExternalLoginCallback,顾名思义,当外部身份验证完成时,它可能是一个很好的挂钩(对我来说 SAML 是一个 "external" 验证)。但是,它似乎在任何时候都没有命中 during/after SAML 身份验证。

答案可能是我们仍然需要自己用旧方法将其拼凑起来,但我希望 WIF 有一些更好的框架挂钩来使这更容易。

public sealed class SomeIdentity : Identity
{
  ...
  // Some custom properties
  public string SomeProperty { get;set;}
}

protected void Application_PostAcquireRequestState(object sender, EventArgs e)
{
  ...
  identity = new SomeIdentity(id, userId);
  // map a claim to a specific property
  identity.SomeProperty = ...Claims[IdpSomePropertyKey];
  ///...

  GenericPrincipal newPrincipal = new GenericPrincipal(identity , null);
  HttpContext.Current.User = newPrincipal;
  System.Threading.Thread.CurrentPrincipal = newPrincipal;
}

现在我正在使用 WIF,我应该将特定于创建自定义 SomeIdentity 的特定身份验证类型(即 Kentor.AuthServices SAML)的代码放在哪里?

我的想法是 SomeIdentity 将成为我应用程序中随处使用的身份 class,但是填充它的属性的代码将需要为每种身份验证类型专门编写,例如使用 SAML 来提取声明并使用它们的值来设置属性。 IE。这是映射发生的地方。

好的,我要试一试,希望我能理解你的问题,而不是对你的代码做出太多假设。我要做的假设是这是一个全新的 MVC 应用程序,使用 Visual Studio ASP.NET 4.6 模板和 "No Authentication" 选项创建(根据我对你的情况的了解,你不是'不想存储这些数据,就像从声明中读取它一样多)。

将名为 Started 的 class 添加到项目的根目录中

using Microsoft.Owin;
using Owin;

[assembly: OwinStartup(typeof(WebApplication1.Startup))]

namespace WebApplication1
{
    public partial class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            ConfigureAuth(app);
        }
    }
}

现在这将是 运行,正如您可能猜到的那样,在 "startup" 上就像 global.asax.cs 文件一样。

现在我们需要实际创建 ConfigureAuth() 方法。这通常是在 App_Start 文件夹中使用名为 "Startup.Auth.cs" 的文件完成的。此文件当前不存在,因此请继续使用此模板创建它

using Owin;
using Kentor.AuthServices.Owin;

namespace WebApplication1
{
    public partial class Startup
    {
        private void ConfigureAuth(IAppBuilder app)
        {
        }
    }
}

这是我们进行身份验证逻辑/设置的地方。 OWIN 带有很多开箱即用的身份验证策略,有些库甚至更多。如果您打算自己编写,我建议您看看 OwinOAuthProviders

继续安装 NuGet 包 Kentor.AuthServices.Owin 包和依赖项。这也应该修复您目前遇到的任何编译错误。您还需要在您的项目中添加对 System.IdentityModel 的引用以备后用。

现在,在 Startup.Auth.cs 中的 ConfigureAuth 方法中,您可以通过执行以下操作将 Kentor 添加到您的应用中。

Public void ConfigureAuth(IAppBuilder app)
{
    var kentorOptions = new KentorAuthServicesAuthenticationOptions(true);
}

现在你的变量 kentorOptions 因为我把它传递给 true,将从你的 WebConfig 读取你的设置。不过,您也可以在此处手动配置它们。

我们感兴趣的部分是 kentorOptions.SPOptions.SystemIdentityModelIdentityConfiguration.ClaimsAuthenticationManager 属性。我们想创建一个自定义的 ClaimsAuthenticationManager 以根据传入的声明提供身份。

创建一个继承自 ClaimsAuthenticationManager

的新 class
using System.Security.Claims;

namespace WebApplication5 {
    public class CustomClaimsAuthManager : ClaimsAuthenticationManager {
        public override ClaimsPrincipal Authenticate( string resourceName, ClaimsPrincipal incomingPrincipal ) {
            ClaimsIdentity ident = (ClaimsIdentity) incomingPrincipal.Identity;
            //Use incomingPrincipal.Identity.AuthenticationType to determine how they got auth'd
            //Use incomingPrincipal.Identity.IsAuthenticated to make sure they are authenticated.
            //Use ident.AddClaim to add a new claim to the user
             ...
            identity = new SomeIdentity( id, userId );
            // map a claim to a specific property
            identity.SomeProperty = ...Claims[IdpSomePropertyKey];
            ///...

            GenericPrincipal newPrincipal = new GenericPrincipal( identity, null );
            return newPrincipal;
        }
    }
}

那是您拥有身份代码的地方。现在最后我们需要将其设置为实际使用的 ClaimsAuthenticationManager,并告诉您的应用程序在 OWIN 管道中使用 Kentor。这都在 Startup.Auth.cs 文件中。

private void ConfigureAuth( IAppBuilder app ) {
            var kentorOptions = new KentorAuthServicesAuthenticationOptions(true);
            kentorOptions.SPOptions.SystemIdentityModelIdentityConfiguration.ClaimsAuthenticationManager = new WebApplication5.CustomClaimsAuthManager();
            app.UseKentorAuthServicesAuthentication( kentorOptions );
        }

Annnndd,希望能做到!对于其他 Auth 提供程序,例如来自 OwinOAuthProviders 的提供程序,您还可以通过覆盖 options.Provider 方法来处理不同事件的事情,例如:

var cookieOptions = new CookieAuthenticationOptions {
                AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
                LoginPath = new PathString( "/Auth/Login" ),
                CookieName = "cooooookiees",
                ExpireTimeSpan = new TimeSpan( 10000, 0, 0, 0, 0 ),
                Provider = new CookieAuthenticationProvider {
                    OnException = context => {
                        var x = context;
                    },
                    OnValidateIdentity = async context => {
                        var invalidateBySecurityStamp = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
                        validateInterval: TimeSpan.FromMinutes( 15 ),
                        regenerateIdentity: ( manager, user ) => user.GenerateUserIdentityAsync( manager ) );
                        await invalidateBySecurityStamp.Invoke( context );

                        if ( context.Identity == null || !context.Identity.IsAuthenticated ) {
                            return;
                        }
                        var newResponseGrant = context.OwinContext.Authentication.AuthenticationResponseGrant;
                        if ( newResponseGrant != null ) {
                            newResponseGrant.Properties.IsPersistent = true;
                        }

                    }
                }
            };
            app.UseCookieAuthentication( cookieOptions );