用户似乎没有在 dotnet core 2.0 中的“Use”管道中进行身份验证
User doesn't seem authenticated in the pipline inside`Use` in dotnetcore 2.0
我正在尝试向 Serilog 提供 ActiveUser
属性。
不幸的是,我似乎无法找到正确的位置来检查当前用户。
在下面的代码中 httpContext.User.Identity.IsAuthenticated
总是 false?
但仅当使用不记名令牌登录时
- 不记名令牌登录工作正常,只要用户是
通过控制器方法的身份验证,用户需要属于
到正确的角色,以便进行身份验证。尽管用户名设置不正确 - 存在声明,并且 IsAuthenticated 设置为 true。
- 如果我使用 cookie 登录,用户设置正确,声明设置正确,Serilog 工作正常。无论是使用承载令牌还是 cookie 调用都是如此。一旦用户使用 cookie 登录,它总是有效的。
当不记名令牌被验证时,用户不是立即设置?
项目是aspnetcore 2.0
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
... (other configuration items)
app.UseIdentityServer();
app.UseAuthentication();
app.Use(async (httpContext, next) =>
{
// HERE IsAuthenticated IS ALWAYS FALSE
// HERE THE CLAIMS ARE ALWAYS EMPTY, UNLESS
// I LOGIN USING THE COOKIE AS WELL - THEN IT WORKS
var userName = httpContext.User.Identity.IsAuthenticated
? httpContext.User.GetClaim("name")
: "(unknown)";
LogContext.PushProperty(
"ActiveUser",
!string.IsNullOrWhiteSpace(userName)
? userName
: "(unknown)");
await next.Invoke();
});
app.UseMvc(
routes =>
{
routes.MapRoute(
"default",
"{controller=Home}/{action=Index}/{id?}");
});
在我的控制器方法中,用户设置正确,并且经过身份验证。
[Authorize]
[HttpGet("user")]
public object UserDetail()
{
// HERE THE CLAIMS ARE SET, IsAuthenticated IS ALWAYS TRUE
// AS THE USER MUST BE AUTHENTICATED TO GET HERE
Debug.Assert(this.User.Identity.IsAuthenticated == true)
编辑
进一步深入研究这个问题,似乎在我的中间件已经执行之后验证了 JWTBearer 令牌。中间件需要在验证令牌后执行。
TL;DR
(完整配置)
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseBrowserLink();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseStaticFiles();
app.UseIdentityServer();
app.UseAuthentication();
app.Use(async (httpContext, next) =>
{
var userName = httpContext.User.Identity.IsAuthenticated
? httpContext.User.GetClaim("email")
: "(unknown)";
LogContext.PushProperty("ActiveUser", !string.IsNullOrWhiteSpace(userName) ? userName : "(unknown)");
await next.Invoke();
});
app.UseMvc(
routes =>
{
routes.MapRoute(
"default",
"{controller=Home}/{action=Index}/{id?}");
});
}
(更多配置)
public void ConfigureServices(IServiceCollection services)
{
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
services.AddAuthentication()
.AddOpenIdConnect(
o =>
{
o.Authority = "https://localhost:44319";
o.ClientId = "api";
o.ClientSecret = "secret";
o.RequireHttpsMetadata = false;
o.ResponseType = "code id_token token";
o.GetClaimsFromUserInfoEndpoint = true;
})
.AddJwtBearer(
o =>
{
o.Authority = "https://localhost:44319";
o.Audience = "api";
o.RequireHttpsMetadata = false;
//o.SaveToken = true;
});
services.AddMemoryCache();
services.AddIdentity<ApplicationUser, ApplicationRole>(
x =>
{
x.Password.RequireNonAlphanumeric = false;
x.Password.RequireUppercase = false;
})
.AddEntityFrameworkStores<FormWorkxContext>()
.AddDefaultTokenProviders()
.AddIdentityServer();
// NB
services.Configure<IdentityOptions>(
options =>
{
options.ClaimsIdentity.RoleClaimType = ClaimTypes.Role;
options.ClaimsIdentity.UserNameClaimType = ClaimTypes.Name;
});
services.ConfigureApplicationCookie(
options =>
{
options.LoginPath = "/login";
options.LogoutPath = "/logout";
options.Events.OnRedirectToLogin = this.ProcessStatusCodeResponse;
});
services.AddIdentityServer()
.AddDeveloperSigningCredential()
.AddInMemoryIdentityResources(Config.GetIdentityResources())
.AddInMemoryApiResources(Config.GetApis())
.AddInMemoryClients(Config.GetClients())
.AddAspNetIdentity<ApplicationUser>();
services.AddTransient<IEmailSender, EmailSender>();
services.AddMvc(
_ =>
{
_.Filters.Add(
new AuthorizeFilter(
new AuthorizationPolicyBuilder(
JwtBearerDefaults.AuthenticationScheme,
IdentityConstants.ApplicationScheme)
.RequireAuthenticatedUser()
.Build()));
_.Filters.Add(new ExceptionFilter());
_.ModelBinderProviders.Insert(0, new PartyModelBinderProvider());
_.ModelBinderProviders.Insert(0, new DbGeographyModelBinder());
_.ModelMetadataDetailsProviders.Add(new KeyTypeModelMetadataProvider());
})
.AddFluentValidation(fv => fv.RegisterValidatorsFromAssemblyContaining<Startup>())
.AddJsonOptions(json => json.SerializerSettings.Converters.Add(new DbGeographyJsonConverter()));
}
我在使用如下设置的主体登录时重现了这个问题:
var principal = new ClaimsPrincipal(new ClaimsIdentity(claims));
然后我使用 SignInAsync
登录。这也导致 User.Identity.Name
有一个值,但 User.Identity.IsAuthenticated
没有被设置为 true。
现在,当我像这样将 authenticationType
参数添加到 ClaimsIdentity
时:
var principal = new ClaimsPrincipal(new ClaimsIdentity(claims, "local"));
IsAuthenticated
现在设置为 true。
我不完全确定您的登录将如何工作,您可以在某处提及此 authenticationType
或者您可以在创建 JWT 时传递它。我就是这样做的。
更新 好吧,刚刚注意到您对名称的评论也没有显示,但您仍然可以尝试设置身份验证类型。另外只要你的主张是正确的,你应该能够使用 AuthenticateAsync
提取原理。一旦您可以从 Context.User
对象访问主体,您始终可以自定义身份验证方案以强制主体。
更新 2 在你的情况下,在你的 AddJwtBearer
中,尝试包括这个:
o.Events.OnTokenValidated = async (context) => {
context.Principal = new ClaimsPrincipal(new ClaimsIdentity(context.Principal.Claims, "local"));
};
正在复制 以防有人遇到此问题并想知道发生了什么:
Since you have multiple authentication schemes registered and none is
the default, authentication does not happen automatically as the
request goes through the pipeline. That's why the HttpContext.User
was empty/unauthenticated when it went through your custom middleware.
In this "passive" mode, the authentication scheme won't be invoked
until it is requested. In your example, this happens when the request
passes through your AuthorizeFilter
. This triggers the JWT
authentication handler, which validates the token, authenticates and
sets the Identity, etc. That's why the User
is populated correctly
by the time it gets to your controller action.
通过添加以下代码行在自定义中间件中显式验证用户:
var result = await context.Request.HttpContext.AuthenticateAsync(JwtBearerDefaults.AuthenticationScheme);//AuthenticationOptions.DefaultAuthenticateScheme)
if (result.Succeeded)
{
//context.User.AddIdentity(result.Principal);
context.User = result.Principal;
}
我正在尝试向 Serilog 提供 ActiveUser
属性。
不幸的是,我似乎无法找到正确的位置来检查当前用户。
在下面的代码中 httpContext.User.Identity.IsAuthenticated
总是 false?
但仅当使用不记名令牌登录时
- 不记名令牌登录工作正常,只要用户是 通过控制器方法的身份验证,用户需要属于 到正确的角色,以便进行身份验证。尽管用户名设置不正确 - 存在声明,并且 IsAuthenticated 设置为 true。
- 如果我使用 cookie 登录,用户设置正确,声明设置正确,Serilog 工作正常。无论是使用承载令牌还是 cookie 调用都是如此。一旦用户使用 cookie 登录,它总是有效的。
当不记名令牌被验证时,用户不是立即设置?
项目是aspnetcore 2.0
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
... (other configuration items)
app.UseIdentityServer();
app.UseAuthentication();
app.Use(async (httpContext, next) =>
{
// HERE IsAuthenticated IS ALWAYS FALSE
// HERE THE CLAIMS ARE ALWAYS EMPTY, UNLESS
// I LOGIN USING THE COOKIE AS WELL - THEN IT WORKS
var userName = httpContext.User.Identity.IsAuthenticated
? httpContext.User.GetClaim("name")
: "(unknown)";
LogContext.PushProperty(
"ActiveUser",
!string.IsNullOrWhiteSpace(userName)
? userName
: "(unknown)");
await next.Invoke();
});
app.UseMvc(
routes =>
{
routes.MapRoute(
"default",
"{controller=Home}/{action=Index}/{id?}");
});
在我的控制器方法中,用户设置正确,并且经过身份验证。
[Authorize]
[HttpGet("user")]
public object UserDetail()
{
// HERE THE CLAIMS ARE SET, IsAuthenticated IS ALWAYS TRUE
// AS THE USER MUST BE AUTHENTICATED TO GET HERE
Debug.Assert(this.User.Identity.IsAuthenticated == true)
编辑
进一步深入研究这个问题,似乎在我的中间件已经执行之后验证了 JWTBearer 令牌。中间件需要在验证令牌后执行。
TL;DR
(完整配置)
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseBrowserLink();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseStaticFiles();
app.UseIdentityServer();
app.UseAuthentication();
app.Use(async (httpContext, next) =>
{
var userName = httpContext.User.Identity.IsAuthenticated
? httpContext.User.GetClaim("email")
: "(unknown)";
LogContext.PushProperty("ActiveUser", !string.IsNullOrWhiteSpace(userName) ? userName : "(unknown)");
await next.Invoke();
});
app.UseMvc(
routes =>
{
routes.MapRoute(
"default",
"{controller=Home}/{action=Index}/{id?}");
});
}
(更多配置)
public void ConfigureServices(IServiceCollection services)
{
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
services.AddAuthentication()
.AddOpenIdConnect(
o =>
{
o.Authority = "https://localhost:44319";
o.ClientId = "api";
o.ClientSecret = "secret";
o.RequireHttpsMetadata = false;
o.ResponseType = "code id_token token";
o.GetClaimsFromUserInfoEndpoint = true;
})
.AddJwtBearer(
o =>
{
o.Authority = "https://localhost:44319";
o.Audience = "api";
o.RequireHttpsMetadata = false;
//o.SaveToken = true;
});
services.AddMemoryCache();
services.AddIdentity<ApplicationUser, ApplicationRole>(
x =>
{
x.Password.RequireNonAlphanumeric = false;
x.Password.RequireUppercase = false;
})
.AddEntityFrameworkStores<FormWorkxContext>()
.AddDefaultTokenProviders()
.AddIdentityServer();
// NB
services.Configure<IdentityOptions>(
options =>
{
options.ClaimsIdentity.RoleClaimType = ClaimTypes.Role;
options.ClaimsIdentity.UserNameClaimType = ClaimTypes.Name;
});
services.ConfigureApplicationCookie(
options =>
{
options.LoginPath = "/login";
options.LogoutPath = "/logout";
options.Events.OnRedirectToLogin = this.ProcessStatusCodeResponse;
});
services.AddIdentityServer()
.AddDeveloperSigningCredential()
.AddInMemoryIdentityResources(Config.GetIdentityResources())
.AddInMemoryApiResources(Config.GetApis())
.AddInMemoryClients(Config.GetClients())
.AddAspNetIdentity<ApplicationUser>();
services.AddTransient<IEmailSender, EmailSender>();
services.AddMvc(
_ =>
{
_.Filters.Add(
new AuthorizeFilter(
new AuthorizationPolicyBuilder(
JwtBearerDefaults.AuthenticationScheme,
IdentityConstants.ApplicationScheme)
.RequireAuthenticatedUser()
.Build()));
_.Filters.Add(new ExceptionFilter());
_.ModelBinderProviders.Insert(0, new PartyModelBinderProvider());
_.ModelBinderProviders.Insert(0, new DbGeographyModelBinder());
_.ModelMetadataDetailsProviders.Add(new KeyTypeModelMetadataProvider());
})
.AddFluentValidation(fv => fv.RegisterValidatorsFromAssemblyContaining<Startup>())
.AddJsonOptions(json => json.SerializerSettings.Converters.Add(new DbGeographyJsonConverter()));
}
我在使用如下设置的主体登录时重现了这个问题:
var principal = new ClaimsPrincipal(new ClaimsIdentity(claims));
然后我使用 SignInAsync
登录。这也导致 User.Identity.Name
有一个值,但 User.Identity.IsAuthenticated
没有被设置为 true。
现在,当我像这样将 authenticationType
参数添加到 ClaimsIdentity
时:
var principal = new ClaimsPrincipal(new ClaimsIdentity(claims, "local"));
IsAuthenticated
现在设置为 true。
我不完全确定您的登录将如何工作,您可以在某处提及此 authenticationType
或者您可以在创建 JWT 时传递它。我就是这样做的。
更新 好吧,刚刚注意到您对名称的评论也没有显示,但您仍然可以尝试设置身份验证类型。另外只要你的主张是正确的,你应该能够使用 AuthenticateAsync
提取原理。一旦您可以从 Context.User
对象访问主体,您始终可以自定义身份验证方案以强制主体。
更新 2 在你的情况下,在你的 AddJwtBearer
中,尝试包括这个:
o.Events.OnTokenValidated = async (context) => {
context.Principal = new ClaimsPrincipal(new ClaimsIdentity(context.Principal.Claims, "local"));
};
正在复制
Since you have multiple authentication schemes registered and none is the default, authentication does not happen automatically as the request goes through the pipeline. That's why the
HttpContext.User
was empty/unauthenticated when it went through your custom middleware. In this "passive" mode, the authentication scheme won't be invoked until it is requested. In your example, this happens when the request passes through yourAuthorizeFilter
. This triggers the JWT authentication handler, which validates the token, authenticates and sets the Identity, etc. That's why theUser
is populated correctly by the time it gets to your controller action.
通过添加以下代码行在自定义中间件中显式验证用户:
var result = await context.Request.HttpContext.AuthenticateAsync(JwtBearerDefaults.AuthenticationScheme);//AuthenticationOptions.DefaultAuthenticateScheme)
if (result.Succeeded)
{
//context.User.AddIdentity(result.Principal);
context.User = result.Principal;
}