在WIF之前,Application_PostAcquireRequestState
是创建自定义标识的好地方,但需要大量框架才能确保您正在进行的每种类型的身份验证都已正确映射。通过自定义标识,我的意思是继承自Identity的类,例如下面的SomeIdentity,这样我们就可以拥有一个特定的属性,我们可能需要所有经过身份验证的用户拥有。
PostAcquirerequestState仍然可用,但是有很多新方法可以挂钩进行身份验证。此外,支持多种身份验证方法时,旧方法变得复杂。
我想知道在WIF中是否有比下面更好的方法来实现这一目标。主要是我想将处理映射声明的代码分离出来。这个想法是代码对于其他身份验证类型/提供者而言是不同的,因为它检索该属性值的方式可能不是来自诸如SAML的声明,而是来自其他地方的其他类型的身份验证方法。我目前正在使用Kentor.AuthServices
进行SAML支持。虽然可能有不同的代码用于根据提供程序映射这些值,但最终结果是已创建SomeIdentity实例并且将设置SomeProperty和其他属性。这样,应用程序的其余部分总是可以依赖/假设已经处理过。
我的MVC项目带有一个AccountController
ExternalLoginCallback
,当外部身份验证完成时(对我来说,SAML是一种“外部”身份验证),名称暗示可能是一个很好的钩子。但是,在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,我应该在哪里放置特定于特定身份验证类型的代码(即Kentor.AuthServices SAML)来创建自定义SomeIdentity?
想法是SomeIdentity将是我的应用程序中所使用的身份类,但填充它的属性的代码将需要专门为每种身份验证类型编写,例如使用SAML来提取声明并使用它们的值来设置这个职业。即它是映射发生的地方。
答案 0 :(得分:4)
好的,我要抓住这个,希望我能理解你的问题而不是对你的代码做太多的假设。我将要做的假设是这是一个全新的MVC应用程序,使用Visual Studio ASP.NET 4.6模板创建,使用"无需身份验证"选项(根据我对您的情况的理解来判断,您不希望存储这些数据,只需从声明中读取数据)。
将名为Started的类添加到项目的根目录
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因为我传递了它,将从你的WebConfig中读取你的设置。您也可以手动配置它们。
我们感兴趣的部分是kentorOptions.SPOptions.SystemIdentityModelIdentityConfiguration.ClaimsAuthenticationManager属性。我们想要创建一个自定义的ClaimsAuthenticationManager来根据传入的声明提供身份。
创建一个继承自ClaimsAuthenticationManager
的新类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的Auth提供程序,您还可以通过覆盖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 );