为什么我的ClaimsIdentity IsAuthenticated始终为false(对于Web api授权过滤器)?


77

在一个Web API项目中,我替代了常规的身份验证过程来检查令牌。代码看起来像这样:

if ( true ) // validate the token or whatever here
{
    var claims = new List<Claim>();
    claims.Add( new Claim( ClaimTypes.Name, "MyUser" ) );
    claims.Add( new Claim( ClaimTypes.NameIdentifier, "MyUserID" ) );
    claims.Add( new Claim( ClaimTypes.Role, "MyRole" ) );

    var claimsIdentity = new ClaimsIdentity( claims );

    var principal = new ClaimsPrincipal( new[] { claimsIdentity } );
    Thread.CurrentPrincipal = principal;
    HttpContext.Current.User = principal;
}

然后,当我将[Authorize]属性应用于控制器时,它无法授权。

调试代码确认相同的行为:

// ALWAYS FALSE!
if ( HttpContext.Current.User.Identity.IsAuthenticated ) {
    // do something
}

为什么即使我已经构建了有效的ClaimsIdentity并将其分配给线程,用户仍未通过身份验证?

Answers:


143

问题是由于.Net 4.5中的重大更改。如本文所述,仅构造声明身份不再使IsAuthenticated返回true。相反,您需要将一些字符串(无关紧要)传递给构造函数。

因此,以上代码中的这一行:

var claimsIdentity = new ClaimsIdentity( claims );

变成这个:

// exact string doesn't matter
var claimsIdentity = new ClaimsIdentity( claims, "CustomApiKeyAuth" );

问题就解决了。更新:请参阅Leo的其他答案。确切的AuthenticationType值可能重要,也可能不重要,具体取决于您在身份验证管道中还有哪些内容。

更新2:如Robin van der Knaap在评论中所建议,其中一个System.Security.Claims.AuthenticationTypes值可能是适当的。

var claimsIdentity = new ClaimsIdentity( claims, AuthenticationTypes.Password );

// and elsewhere in your application...
if (User.Identity.AuthenticationType == AuthenticationTypes.Password) {
    // ...
}

9
尽管可以添加任何字符串,但是根据MSDN,这通常应为AuthenticationTypes类中定义的值之一。msdn.microsoft.com/en-us/library/…–
罗宾·范德纳纳普

1
示例:var ClaimsIdentity = new ClaimsIdentity(Claims,AuthenticationTypes.Password);
罗宾·范德·纳纳普

3
字符串的值在User.Identity.AuthenticationType中变得可见
Robin van der Knaap 2014年

哇,真是晦涩!感谢您在这里分享!我被困了一个多小时。
DavidEdwards

15

尽管所提供的答案具有一定的有效性,但并不完全正确。您不能假设仅添加任何字符串都能神奇地工作。如注释之一所述,该字符串必须与以下AuthenticationTypes枚举之一匹配,而该枚举又必须与OWIN身份验证/授权中间件中指定的枚举...例如。

public void ConfigureOAuth(IAppBuilder app)
        {
            app.UseCors(CorsOptions.AllowAll);

            OAuthAuthorizationServerOptions serverOptions = new OAuthAuthorizationServerOptions()
            {
                AllowInsecureHttp = true,
                TokenEndpointPath = new Microsoft.Owin.PathString("/token"),
                AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
                AuthenticationType = AuthenticationTypes.Password,
                AuthenticationMode = Microsoft.Owin.Security.AuthenticationMode.Active,
                Provider = new AppAuthServerProvider()
            };


            app.UseOAuthAuthorizationServer(serverOptions);
            app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions()
                {
                    AuthenticationMode = Microsoft.Owin.Security.AuthenticationMode.Active,
                    AuthenticationType = AuthenticationTypes.Password
                });            
        }

但是,在上述情况下,这没什么大不了的。但是,如果您使用更多身份验证/授权级别,则声明将与匹配相同级别的声明相关联AuthenticationType...另一个示例是当您使用Cookie身份验证时...

public void Configuration(IAppBuilder app)
        {
            app.UseCookieAuthentication(new CookieAuthenticationOptions
            {
                AuthenticationType = "ApplicationCookie",
                LoginPath = new PathString("/auth/login")
            });
        }

其中AuthenticationType描述了Cookie的名称,因为您的应用可能已从其他提供商那里获得了其他Cookie,所以AuthenticationType在实例化声明时设置,以便将其关联到正确的Cookie是很重要的


3
在.NET Core中,您可以使用常量AuthenticationType,例如CookieAuthenticationDefaults.AuthenticationSchemeJwtBearerDefaults.AuthenticationScheme
亚历克斯·克劳斯

2
请注意,在创建时ClaimsIdentity,您需要将模式名称作为传递AuthenticationType(如new ClaimsIdentity(claims, AuthenticationScheme))。否则IsAuthenticated身份上的标志将会是false。在过去的两年中,我两次遇到相同的非直觉性问题,这个答案再次帮助了我。不幸的是,无法两次回答这个答案。
亚历克斯·克劳斯
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.