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

Why is my ClaimsIdentity IsAuthenticated always false (for web api Authorize filter)?

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

1
2
3
4
5
6
7
8
9
10
11
12
13
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]属性应用于控制器时,它无法授权。

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

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

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


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

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

1
var claimsIdentity = new ClaimsIdentity( claims );

变成这个:

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

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

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

1
2
3
4
5
6
var claimsIdentity = new ClaimsIdentity( claims, AuthenticationTypes.Password );

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


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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身份验证...

1
2
3
4
5
6
7
8
public void Configuration(IAppBuilder app)
        {
            app.UseCookieAuthentication(new CookieAuthenticationOptions
            {
                AuthenticationType ="ApplicationCookie",
                LoginPath = new PathString("/auth/login")
            });
        }

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