在身份验证失败时返回自定义状态码 .Net Core

Posted

技术标签:

【中文标题】在身份验证失败时返回自定义状态码 .Net Core【英文标题】:Return custom status code on authentication failure .Net Core 【发布时间】:2021-09-27 08:19:47 【问题描述】:

我在 .Net 核心中有一个自定义身份验证方案,我想针对某些特定类型的身份验证失败返回特定响应(而不是仅返回 401)。我目前的做法是这样的:

protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
    
        var token = GetToken();
        if (string.IsNullOrWhiteSpace(token))
        
            return AuthenticateResult.NoResult();
        

        var validationResult = _tokenContextProvider.Validate(); // validate

        if (validationResult != TokenValidationResult.Ok)
        
            await updateResponseCode(Context, validationResult);
            return AuthenticateResult.Fail("Token invalid");
        
        
        //success
        var userContext = tokenContextProvider.GetUserContext();
        var ticket = GetAuthenticationTicket(userContext);
        return AuthenticateResult.Success(ticket);
    


    private static async Task updateResponseCode(HttpContext context, TokenValidationResult validationResult)
    
        if (!context.Response.HasStarted)
        
            if (validationResult == TokenValidationResult.SpecialError)
            
                context.Response.StatusCode = (int)HttpStatusCode.Forbidden;
                var errorMessage = JsonConvert.SerializeObject(new
                
                    error = new
                   
                      message = validationResult.Message
                    
                 );
                context.Response.Headers["Content-Type"] = "application/json";
                await context.Response.WriteAsync(errorMessage, Encoding.UTF8);                
          
        
    

这很好,并返回我想要的响应代码。但是,另一位中间件(我假设)随后尝试设置 StatusCode 并导致记录一些异常。

Microsoft.AspNetCore.Server.IIS.Core.IISHttpContext in ThrowResponseAlreadyStartedException

System.InvalidOperationException:无法设置 StatusCode,因为响应已经开始。

我可以“忽略”异常,但我宁愿找到一些更清晰支持的方法来执行此操作,这样我就不会因为这些异常而使日志混乱。

是否有其他方法可以更改身份验证失败后返回的响应?

【问题讨论】:

这不是身份验证方案的责任。通常的方法是提供可以在身份验证配置期间挂钩的事件。见CookieAuthenticationHandler:source.dot.net/#Microsoft.AspNetCore.Authentication.Cookies/… 您找到解决方案了吗? @MightyAtom 我确实找到了解决方案。我会发布我最终做了什么 【参考方案1】:

如果有人遇到类似的问题,我基本上采取了不同的方法。我没有试图强制身份验证处理程序做一些它不适合做的事情,而是将StatusCode 设置逻辑拉到它自己的中间件中。

在认证过程中,当我关心的事件发生时,我像这样向HttpContext添加一个项目

 protected override Task<AuthenticateResult> HandleAuthenticateAsync()
    
        var token = GetToken();
        ...

        if (validationResult != TokenValidationResult.Ok)
        
            Context.Items.Add("TokenValidationResult", validationResult);
            return Task.FromResult(AuthenticateResult.Fail("Token invalid"));
         

然后中间件有机会在其他处理程序运行后运行

public async Task Invoke(HttpContext httpContext)
    
        await _next(httpContext);
        // context Item ready here
        var tokenValidationResult = getTokenValidationResult(httpContext);
        if (tokenValidationResult != null && tokenValidationResult != TokenValidationResult.Ok)
        
            await updateResponseCode(httpContext, tokenValidationResult);
        
    

    private static async Task updateResponseCode(HttpContext context, TokenValidationResult validationResult)
    
        if (!context.Response.HasStarted)
        
            if (validationResult == TokenValidationResult.SpecialError)
            
                await setErrorMessageAndCode(context, HttpStatusCode.Gone, validationResult);
            
            else
            
                await setErrorMessageAndCode(context, HttpStatusCode.Forbidden, validationResult);
            
        
    

!context.Response.HasStarted 的检查仍然是必需的,但现在我可以动态更新状态代码而不会影响下游。我注册了中间件在身份验证之前运行(所以它在退出的路上运行)

app.UseCustomJwtStatusCodes();
app.UseAuthentication();
app.UseAuthorization();

这对我有用,允许我设置错误代码而不会干扰任何其他中间件。

【讨论】:

以上是关于在身份验证失败时返回自定义状态码 .Net Core的主要内容,如果未能解决你的问题,请参考以下文章

使用 Laravel 和 Passport 在身份验证失败时响应状态码 401?

Firebase 电话身份验证在已投入生产的应用程序上失败,状态码为 17028

用户未经授权时的 Laravel 护照自定义错误消息和状态码

ServiceStack 对身份验证失败的自定义响应

Django,自定义身份验证登录。身份验证失败时如何显示错误消息?

API 设计 - 客户端请求中的可选正文 - 验证失败时返回的状态码