【问题标题】:ASP.NET Web Api 2 automatically add header on exceptionASP.NET Web Api 2 在异常时自动添加标头
【发布时间】:2019-03-04 01:01:49
【问题描述】:

我目前关注this tutorial 实现 Jwt 刷新令牌。目前,当我在响应 API 请求时遇到特定异常时,我正在尝试添加一个名为 Token-Expired : "true" 的标头。

在本教程中,本节展示了如何在 Startup.cs 中进行操作

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc();
    //...

    services.AddAuthentication(options =>
    {
        options.DefaultAuthenticateScheme = "bearer";
        options.DefaultChallengeScheme = "bearer";
    }).AddJwtBearer("bearer", options =>
    {
        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateAudience = false,
            ValidateIssuer = false,
            ValidateIssuerSigningKey = true,
            IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("the server key used to sign the JWT token is here, use more than 16 chars")),
            ValidateLifetime = true,
            ClockSkew = TimeSpan.Zero //the default for this setting is 5 minutes
        };
        options.Events = new JwtBearerEvents
        {
            OnAuthenticationFailed = context =>
            {
                if (context.Exception.GetType() == typeof(SecurityTokenExpiredException))
                {
                    context.Response.Headers.Add("Token-Expired", "true");
                }
                return Task.CompletedTask;
            }
        };
    });
}

问题是我使用的是 ASP.NET Web Api 2 而不是 .net core 2.1。如何将此代码添加到我的?我认为可能可行的一种方法是我可以将它添加到我的 TokenValidation 类中,但我不知道该怎么做:

public class TokenValidationHandler : DelegatingHandler
{
    private static bool RetrieveToken(HttpRequestMessage request, out string token)
    {
        token = null;
        IEnumerable<string> authHeaders;
        if (!request.Headers.TryGetValues("Authorization", out authHeaders) || authHeaders.Count() > 1)
        {
            return false;
        }
        var bearerToken = authHeaders.ElementAt(0);
        token = bearerToken.StartsWith("Bearer ") ? bearerToken.Substring(7) : bearerToken;
        return true;
    }

    protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        HttpStatusCode statusCode;
        string token;
        //determine whether a jwt exists or not
        if (!RetrieveToken(request, out token))
        {
            statusCode = HttpStatusCode.Unauthorized;
            //allow requests with no token - whether a action method needs an authentication can be set with the claimsauthorization attribute
            return base.SendAsync(request, cancellationToken);
        }

        try
        {
            const string sec = HostConfig.SecurityKey;
            var now = DateTime.UtcNow;
            var securityKey = new SymmetricSecurityKey(System.Text.Encoding.Default.GetBytes(sec));


            SecurityToken securityToken;
            JwtSecurityTokenHandler handler = new JwtSecurityTokenHandler();
            TokenValidationParameters validationParameters = new TokenValidationParameters()
            {
                ValidAudience = HostConfig.Audience,
                ValidIssuer = HostConfig.Issuer,
                //Set false to ignore expiration date
                ValidateLifetime = false,
                ValidateIssuerSigningKey = true,
                LifetimeValidator = this.LifetimeValidator,
                IssuerSigningKey = securityKey
            };
            //extract and assign the user of the jwt
            Thread.CurrentPrincipal = handler.ValidateToken(token, validationParameters, out securityToken);
            HttpContext.Current.User = handler.ValidateToken(token, validationParameters, out securityToken);

            return base.SendAsync(request, cancellationToken);
        }
        catch (SecurityTokenValidationException e)
        {
            statusCode = HttpStatusCode.Unauthorized;
        }
        catch (Exception ex)
        {
            statusCode = HttpStatusCode.InternalServerError;
        }
        return Task<HttpResponseMessage>.Factory.StartNew(() => new HttpResponseMessage(statusCode) { });
    }

    public bool LifetimeValidator(DateTime? notBefore, DateTime? expires, SecurityToken securityToken, TokenValidationParameters validationParameters)
    {
        if (expires != null)
        {
            if (DateTime.UtcNow < expires) return true;
        }
        return false;
    }
}

【问题讨论】:

  • 我觉得差不多?
  • 是的,几乎一样
  • 是的,但现在我只需要知道如何在全球范围内添加我的标题内容
  • 它已经在全球不是吗?
  • 没有。我想像第一个代码块一样添加它

标签: c# asp.net asp.net-web-api asp.net-core-webapi


【解决方案1】:

请再添加一个 SecurityTokenExpiredException 捕获块来捕获令牌过期错误,并在捕获块中添加响应标头,如下所示。

public class TokenValidationHandler : DelegatingHandler
{
    private static bool RetrieveToken(HttpRequestMessage request, out string token)
    {
        token = null;
        IEnumerable<string> authHeaders;
        if (!request.Headers.TryGetValues("Authorization", out authHeaders) || authHeaders.Count() > 1)
        {
            return false;
        }
        var bearerToken = authHeaders.ElementAt(0);
        token = bearerToken.StartsWith("Bearer ") ? bearerToken.Substring(7) : bearerToken;
        return true;
    }

    protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        HttpResponseMessage response = new HttpResponseMessage();
        string token;
        //determine whether a jwt exists or not
        if (!RetrieveToken(request, out token))
        {
            response.StatusCode = HttpStatusCode.Unauthorized;
            //allow requests with no token - whether a action method needs an authentication can be set with the claimsauthorization attribute
            return base.SendAsync(request, cancellationToken);
        }

        try
        {
            const string sec = HostConfig.SecurityKey;
            var now = DateTime.UtcNow;
            var securityKey = new SymmetricSecurityKey(System.Text.Encoding.Default.GetBytes(sec));


            SecurityToken securityToken;
            JwtSecurityTokenHandler handler = new JwtSecurityTokenHandler();
            TokenValidationParameters validationParameters = new TokenValidationParameters()
            {
                ValidAudience = HostConfig.Audience,
                ValidIssuer = HostConfig.Issuer,
                //Set false to ignore expiration date
                ValidateLifetime = false,
                ValidateIssuerSigningKey = true,
                LifetimeValidator = this.LifetimeValidator,
                IssuerSigningKey = securityKey
            };
            //extract and assign the user of the jwt
            Thread.CurrentPrincipal = handler.ValidateToken(token, validationParameters, out securityToken);
            HttpContext.Current.User = handler.ValidateToken(token, validationParameters, out securityToken);

            return base.SendAsync(request, cancellationToken);
        }
        catch (SecurityTokenExpiredException e)
        {
            var expireResponse = base.SendAsync(request, cancellationToken).Result;
            response.Headers.Add("Token-Expired", "true");
            response.StatusCode = HttpStatusCode.Unauthorized;

        }
        catch (SecurityTokenValidationException e)
        {
            response.StatusCode = HttpStatusCode.Unauthorized;
        }
        catch (Exception ex)
        {
            response.StatusCode = HttpStatusCode.InternalServerError;
        }
        return Task<HttpResponseMessage>.Factory.StartNew(() => response);
    }

    public bool LifetimeValidator(DateTime? notBefore, DateTime? expires, SecurityToken securityToken, TokenValidationParameters validationParameters)
    {
        if (expires != null)
        {
            if (DateTime.UtcNow < expires) return true;
        }
        return false;
    }


}

【讨论】:

    【解决方案2】:

    您还需要先安装 Microsoft.Owin.Host.SystemWeb 包。然后创建一个名为 Startup.cs 的类

    这会对你有所帮助..

    public class Startup
    {
       public void Configuration(IAppBuilder app)
       {
          //
       }
       public void ConfigureServices(IServiceCollection services)
       {
        services.AddMvc();
        //...
    
        services.AddAuthentication(options =>
        {
            options.DefaultAuthenticateScheme = "bearer";
            options.DefaultChallengeScheme = "bearer";
        }).AddJwtBearer("bearer", options =>
        {
            options.TokenValidationParameters = new TokenValidationParameters
            {
                ValidateAudience = false,
                ValidateIssuer = false,
                ValidateIssuerSigningKey = true,
                IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("the server key used to sign the JWT token is here, use more than 16 chars")),
                ValidateLifetime = true,
                ClockSkew = TimeSpan.Zero //the default for this setting is 5 minutes
            };
            options.Events = new JwtBearerEvents
            {
                OnAuthenticationFailed = context =>
                {
                    if (context.Exception.GetType() == typeof(SecurityTokenExpiredException))
                    {
                        context.Response.Headers.Add("Token-Expired", "true");
                    }
                    return Task.CompletedTask;
                }
            };
        });
    }
    }
    

    除此之外,如果您的启动类不知何故不在您的默认名称空间中,请在 &lt;appSettings&gt; 区域添加一个 Web 配置行,例如:&lt;add key="owin:AppStartup" value="[NameSpace].Startup" /&gt;

    要使用ConfigureServices 方法,您需要内置依赖注入,仅在 ASP.NET Core 中可用。您将需要使用第三方 IoC 容器,例如 -

    用于 Web API 的 Autofac

    忍者

    为了得到下面的库。

    Microsoft.Extensions.DependencyInjection

    【讨论】:

    • 您从哪里获得 Iservicecollection?
    • 我认为您的解决方案适用于 ASP.NET Core 1.0 及更高版本。
    • 你需要使用依赖注入。查看编辑后的答案
    猜你喜欢
    • 2014-03-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-06-12
    • 2011-11-04
    • 2018-02-21
    • 2017-02-01
    • 2019-05-29
    相关资源
    最近更新 更多