在ASP.NET Core中,身份验证后是否有一种方法可以在中间件中添加声明?

43

我在我的创业公司中有这个:

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseHsts();
    }

    app.UseHttpsRedirection();
    app.UseStaticFiles();
    app.UseSwaggerWithUi();

    app.UseAuthentication();
    app.UseMiddleware<SomeMiddleware>();

    app.UseMvc();
}

用户验证后,我需要添加一些额外的声明,但是中间件Invoke函数总是在Auth之前触发(HttpContext.User.Identity.IsAuthenticated为false)。但当它到达控制器时,用户已经通过身份验证。

这里有什么想法吗?我尝试在调用app.UseMiddleware后放置“app.UseAuthentication()”,但没有任何影响。

我目前正在使用多个身份验证方案。我不确定是否会产生影响。


你找到解决方案了吗?我遇到了完全相同的问题(两个JWT方案,没有默认值),并且我的中间件中也总是得到用户未经身份验证的错误。 - jpgrassi
2
对于那些遇到相同问题的人,这个答案解释了为什么会发生这种情况:https://dev59.com/EKXja4cB1Zd3GeqPO0mZ#46309119。因此,我将我的 app.UseMiddleware<SomeMiddleware>(); 移动到了 app.UseAuthorization() 之后。 - jpgrassi
5个回答

47

可以做到,但是你需要添加一个新类型为ClaimsIdentity的身份验证。

public class SomeMiddleware
{
    private readonly RequestDelegate _next;

    public SomeMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    public async Task InvokeAsync(HttpContext httpContext)
    {
        if (httpContext.User != null && httpContext.User.Identity.IsAuthenticated)
        {
            var claims = new List<Claim>
            {
                new Claim("SomeClaim", "SomeValue")
            };

            var appIdentity = new ClaimsIdentity(claims);
            httpContext.User.AddIdentity(appIdentity);                
        }

        await _next(httpContext);
    }
}

如果httpContext.User.Identity.IsAuthenticated始终为false,那么这个怎么工作? - Michael Esteves
2
这个中间件必须在Configure方法中的UseAuthentication中间件之后添加,该中间件首先对用户进行身份验证。如果用户没有为此目的进行身份验证,但您仍然需要设置声明(不确定是否有意义),则可以删除此条件或实现不同的逻辑。 - Alexey Andrushkevich
26
“但是,你不需要再创建一个新的声明列表,而是需要添加一个新的身份类型。”实际上并非如此。你可以将声明添加到现有的身份验证中。HttpContext.User.Identities.FirstOrDefault().AddClaim(new Claim("user_id", "hello world")); - Enrico

18

您可以编写自己的中间件以添加新声明。

public class YourCustomMiddleware
{
    private readonly RequestDelegate _next;

    public YourCustomMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    public async Task InvokeAsync(HttpContext httpContext)
    {
        if (httpContext.User != null && httpContext.User.Identity.IsAuthenticated)
        {

            httpContext.User.Identities.FirstOrDefault().AddClaim(new Claim("your claim", "your field"));
        }
        await _next(httpContext);
    }
}

在您的应用程序启动中

app.UseAuthentication();
app.UseMiddleware<YourCustomMiddleware>();

14

您可以在UseAuthentication()之后立即添加另一个中间件来添加声明:

app.UseAuthentication();
app.Use(async(context, next)=>{
    if(context.User !=null && context.User.Identity.IsAuthenticated){
        // add claims here 
        context.User.Claims.Append(new Claim("type-x","value-x"));
    }
    await next();
});

//  call other middlewares 
app.UseMiddleware<SomeMiddleware>();

2
尝试过了,不行。只有在我有默认身份验证方案的情况下才有效。 - Yodacheese
据我所知,AuthenticationMiddleware会为每个请求调用。声明将在身份验证之后、任何其他中间件之前添加。它不关心是否有默认方案。您能详细说明一下“只有在我有默认的身份验证方案时才有效”吗? - itminus
当我刷新页面时,这些声明不知何故消失了。有什么想法为什么会这样? - Enrico
5
@Enrico 的 context.User.Claims.Append 返回另一个列表。 - FindOutIslamNow

9
.NET Core 2.x的首选方法是使用IClaimsTransformation,它具有单个方法TransformAsync(ClaimsPrincipal),带有注释:
提供一个中央转换点来更改指定的主体。注意:这将在每个AuthenticateAsync调用上运行,因此如果您的转换不是幂等的,则返回新的ClaimsPrincipal更安全。
根据我添加的增强的性质,我可以将声明添加到现有的已验证标识中,或者创建一个新的标识并将其标记为已验证。使用第二个想法,您可以通过在尝试增强之前检查自定义标识符来使您的方法成为幂等的。

11
你能否添加更多关于如何实际使用它的信息?最好提供一个例子。 - empz
阅读更多 https://learn.microsoft.com/en-us/aspnet/core/security/authentication/samples?view=aspnetcore-2.1GitHub 代码 https://github.com/aspnet/AspNetCore/blob/release/2.2/src/Security/samples/ClaimsTransformation/ClaimsTransformer.cs - Sanket Sonavane
有前途,但是我仍然没有找到一种持久化声明的方法(可能是添加到现有标识,不过也可能有另外一种通过cookie/token持久化它的方法)。所以我同意需要更多信息。感谢Sanket提供链接,但其中一个链接已经失效了,微软的链接并没有讨论如何持久化声明,只是拦截了对它的调用。将其添加到传递给TransformAsync的ClaimsPrincipal中不起作用,而将其添加到传入的ClaimsPrincipal的Identity中也不行,因为在编译时它只是具有无法声明的IIdentity。 - EGP
1
看一下IDistributedCache,它有一个内存版本,或者如果你需要多节点/区域等,你可以拥有一个Redis实例。基本上,你可以在从数据库检索的服务上放置一个缓存版本,即一个装饰器,类似Polly这样的工具可以帮助你实现。 - Paul Hatcher
发现了一个使用 IClaimsTransformation 的示例 https://gunnarpeipman.com/aspnet-core-adding-claims-to-existing-identity/ - Rajshekar Reddy
显示剩余2条评论

2

这取决于你想做什么以及你使用的方案。

例如,如果您使用JwtBearer,那么您可以利用JwtBearerOptions.Events来处理中间件引发的特定事件。您需要在Startup类的ConfigureServices方法中设置它。

这将为您提供更精细的控制,以确定要将声明添加到哪个特定的情况中,例如OnTokenValidated


2
这个方案还可以,但有点繁琐。我有多个方案,所以JWT只是其中之一,然后我需要在自定义认证方案上添加事件,同时我仍然想使用依赖注入,所以我必须解决它。 - Yodacheese

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接