Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/309.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 无效或过期的令牌返回错误_C#_.net_Oauth_Oauth 2.0_Owin - Fatal编程技术网

C# 无效或过期的令牌返回错误

C# 无效或过期的令牌返回错误,c#,.net,oauth,oauth-2.0,owin,C#,.net,Oauth,Oauth 2.0,Owin,我正在尝试用Owin实现OAuth承载身份验证。当传递无效或过期的令牌时,默认实现是将其记录为警告,只是不设置标识。然而,在这种情况下,我想以一个错误拒绝整个请求。但是我该怎么做呢 在深入研究代码之后,我发现在OAuthBeareAuthenticationHandler中,当提供的AuthenticationTokenProvider未解析任何票证时(如默认实现),它将使用回退机制解析令牌。当令牌无法解析为任何票证或令牌过期时,此处理程序将记录警告 但是我找不到任何地方来插入我自己的逻辑,当令

我正在尝试用Owin实现OAuth承载身份验证。当传递无效或过期的令牌时,默认实现是将其记录为警告,只是不设置标识。然而,在这种情况下,我想以一个错误拒绝整个请求。但是我该怎么做呢

在深入研究代码之后,我发现在
OAuthBeareAuthenticationHandler
中,当提供的
AuthenticationTokenProvider
未解析任何票证时(如默认实现),它将使用回退机制解析令牌。当令牌无法解析为任何票证或令牌过期时,此处理程序将记录警告

但是我找不到任何地方来插入我自己的逻辑,当令牌无效或过期时会发生什么。理论上,我可以在
AuthenticationTokenProvider
中自己检查这一点,但随后我必须重新实现创建和读取令牌的逻辑(=复制它)。而且这似乎不合适,因为这个类似乎只负责创建和解析令牌。我也看不到在
OAuthBeareAuthenticationMiddleware
中插入自己的
OAuthBeareAuthenticationHandler
实现的方法

显然,我最好、最干净的办法是重新实现整个中间件,但这似乎也太过分了

我忽略了什么?我该怎么做才是最好的

编辑:


请澄清。我知道,如果不设置标识,请求将被拒绝,稍后将在Web API中使用401 Unauthorized。但我个人认为这是非常糟糕的风格,在没有任何通知的情况下默默地吞下一个错误的访问令牌。这样你就不会知道你的令牌是垃圾,你只是知道你没有被授权。

如果身份验证失败(意味着令牌过期),那么该层就不会设置用户,就像你说的那样。由授权层(稍后)来拒绝呼叫。因此,对于您的场景,您的Web API需要拒绝匿名调用方的访问。使用[Authorize]authorization filter属性。

我遇到了类似的问题,我认为答案是太晚了,但有人会带着类似的问题来到这里:

我使用这个nuget包来验证身份验证,但我认为任何方法都可以帮助:。你可以在这个网站上阅读它的文档

AuthenticationFilter.cs

public class AuthenticationFilter : AuthenticationFilterAttribute{
public override void OnAuthentication(HttpAuthenticationContext context)
{
    System.Net.Http.Formatting.MediaTypeFormatter jsonFormatter = new System.Net.Http.Formatting.JsonMediaTypeFormatter();
    var ci = context.Principal.Identity as ClaimsIdentity;

    //First of all we are going to check that the request has the required Authorization header. If not set the Error
    var authHeader = context.Request.Headers.Authorization;
    //Change "Bearer" for the needed schema
    if (authHeader == null || authHeader.Scheme != "Bearer")
    {
        context.ErrorResult = context.ErrorResult = new AuthenticationFailureResult("unauthorized", context.Request,
            new { Error = new { Code = 401, Message = "Request require authorization" } });
    }
    //If the token has expired the property "IsAuthenticated" would be False, then set the error
    else if (!ci.IsAuthenticated)
    {
        context.ErrorResult = new AuthenticationFailureResult("unauthorized", context.Request,
            new { Error = new { Code = 401, Message = "The Token has expired" } });
    }
}}
AuthenticationFailureResult.cs

public class AuthenticationFailureResult : IHttpActionResult{
private object ResponseMessage;
public AuthenticationFailureResult(string reasonPhrase, HttpRequestMessage request, object responseMessage)
{
    ReasonPhrase = reasonPhrase;
    Request = request;
    ResponseMessage = responseMessage;
}

public string ReasonPhrase { get; private set; }

public HttpRequestMessage Request { get; private set; }

public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
    return Task.FromResult(Execute());
}

private HttpResponseMessage Execute()
{
    HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.Unauthorized);
    System.Net.Http.Formatting.MediaTypeFormatter jsonFormatter = new System.Net.Http.Formatting.JsonMediaTypeFormatter();
    response.Content = new System.Net.Http.ObjectContent<object>(ResponseMessage, jsonFormatter);
    response.RequestMessage = Request;
    response.ReasonPhrase = ReasonPhrase;
    return response;
}}
字体和灵感文档:

//github.com/mbenford/WebApi-AuthenticationFilter


//www.asp.net/web api/overview/security/authentication filters

是的,我没有找到这个“好”的解决方案

我也没有找到一种方法来插入我自己的 中的OAuthBeareAuthenticationHandler OAuthBeareAuthenticationMiddleware

显然,我最好最干净的办法就是重新实现整个计划 中间件,但这似乎也太过分了

同意,但我就是这么做的(在读你的帖子之前)。我复制并粘贴了三个owin类,并使其在Owins上下文中设置属性,以后其他处理程序可以检查该属性

public static class OAuthBearerAuthenticationExtensions
{
    public static IAppBuilder UseOAuthBearerAuthenticationExtended(this IAppBuilder app, OAuthBearerAuthenticationOptions options)
    {
        if (app == null)
            throw new ArgumentNullException(nameof(app));

        app.Use(typeof(OAuthBearerAuthenticationMiddlewareExtended), app, options);
        app.UseStageMarker(PipelineStage.Authenticate);
        return app;
    }
}

internal class OAuthBearerAuthenticationHandlerExtended : AuthenticationHandler<OAuthBearerAuthenticationOptions>
{
    private readonly ILogger _logger;
    private readonly string _challenge;

    public OAuthBearerAuthenticationHandlerExtended(ILogger logger, string challenge)
    {
        _logger = logger;
        _challenge = challenge;
    }

    protected override async Task<AuthenticationTicket> AuthenticateCoreAsync()
    {
        try
        {
            // Find token in default location
            string requestToken = null;
            string authorization = Request.Headers.Get("Authorization");
            if (!string.IsNullOrEmpty(authorization))
            {
                if (authorization.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase))
                {
                    requestToken = authorization.Substring("Bearer ".Length).Trim();
                }
            }

            // Give application opportunity to find from a different location, adjust, or reject token
            var requestTokenContext = new OAuthRequestTokenContext(Context, requestToken);
            await Options.Provider.RequestToken(requestTokenContext);

            // If no token found, no further work possible
            if (string.IsNullOrEmpty(requestTokenContext.Token))
            {
                return null;
            }

            // Call provider to process the token into data
            var tokenReceiveContext = new AuthenticationTokenReceiveContext(
                Context,
                Options.AccessTokenFormat,
                requestTokenContext.Token);

            await Options.AccessTokenProvider.ReceiveAsync(tokenReceiveContext);
            if (tokenReceiveContext.Ticket == null)
            {
                tokenReceiveContext.DeserializeTicket(tokenReceiveContext.Token);
            }

            AuthenticationTicket ticket = tokenReceiveContext.Ticket;
            if (ticket == null)
            {
                _logger.WriteWarning("invalid bearer token received");
                Context.Set("oauth.token_invalid", true);
                return null;
            }

            // Validate expiration time if present
            DateTimeOffset currentUtc = Options.SystemClock.UtcNow;

            if (ticket.Properties.ExpiresUtc.HasValue &&
                ticket.Properties.ExpiresUtc.Value < currentUtc)
            {
                _logger.WriteWarning("expired bearer token received");
                Context.Set("oauth.token_expired", true);
                return null;
            }

            // Give application final opportunity to override results
            var context = new OAuthValidateIdentityContext(Context, Options, ticket);
            if (ticket != null &&
                ticket.Identity != null &&
                ticket.Identity.IsAuthenticated)
            {
                // bearer token with identity starts validated
                context.Validated();
            }
            if (Options.Provider != null)
            {
                await Options.Provider.ValidateIdentity(context);
            }
            if (!context.IsValidated)
            {
                return null;
            }

            // resulting identity values go back to caller
            return context.Ticket;
        }
        catch (Exception ex)
        {
            _logger.WriteError("Authentication failed", ex);
            return null;
        }
    }

    protected override Task ApplyResponseChallengeAsync()
    {
        if (Response.StatusCode != 401)
        {
            return Task.FromResult<object>(null);
        }

        AuthenticationResponseChallenge challenge = Helper.LookupChallenge(Options.AuthenticationType, Options.AuthenticationMode);

        if (challenge != null)
        {
            OAuthChallengeContext challengeContext = new OAuthChallengeContext(Context, _challenge);
            Options.Provider.ApplyChallenge(challengeContext);
        }

        return Task.FromResult<object>(null);
    }
}


public class OAuthBearerAuthenticationMiddlewareExtended : AuthenticationMiddleware<OAuthBearerAuthenticationOptions>
{
    private readonly ILogger _logger;
    private readonly string _challenge;

    /// <summary>
    /// Bearer authentication component which is added to an OWIN pipeline. This constructor is not
    ///             called by application code directly, instead it is added by calling the the IAppBuilder UseOAuthBearerAuthentication
    ///             extension method.
    /// 
    /// </summary>
    public OAuthBearerAuthenticationMiddlewareExtended(OwinMiddleware next, IAppBuilder app, OAuthBearerAuthenticationOptions options)
      : base(next, options)
    {
        _logger = AppBuilderLoggerExtensions.CreateLogger<OAuthBearerAuthenticationMiddlewareExtended>(app);
        _challenge = string.IsNullOrWhiteSpace(Options.Challenge) ? (!string.IsNullOrWhiteSpace(Options.Realm) ? "Bearer realm=\"" + this.Options.Realm + "\"" : "Bearer") : this.Options.Challenge;

        if (Options.Provider == null)
            Options.Provider = new OAuthBearerAuthenticationProvider();

        if (Options.AccessTokenFormat == null)
            Options.AccessTokenFormat = new TicketDataFormat(
                Microsoft.Owin.Security.DataProtection.AppBuilderExtensions.CreateDataProtector(app, typeof(OAuthBearerAuthenticationMiddleware).Namespace, "Access_Token", "v1"));

        if (Options.AccessTokenProvider != null)
            return;

        Options.AccessTokenProvider = new AuthenticationTokenProvider();
    }

    /// <summary>
    /// Called by the AuthenticationMiddleware base class to create a per-request handler.
    /// 
    /// </summary>
    /// 
    /// <returns>
    /// A new instance of the request handler
    /// </returns>
    protected override AuthenticationHandler<OAuthBearerAuthenticationOptions> CreateHandler()
    {
        return new OAuthBearerAuthenticationHandlerExtended(_logger, _challenge);
    }
}
我的configureOAuth是什么样子的:

public void ConfigureOAuth(IAppBuilder app)
{
    app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);

    OAuthBearerOptions = new OAuthBearerAuthenticationOptions()
    {

    };

    OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions()
    {
        AllowInsecureHttp = true,
        TokenEndpointPath = new PathString("/token"),
        AccessTokenExpireTimeSpan = TimeSpan.FromSeconds(10),

        Provider = new SimpleAuthorizationServerProvider(),
        RefreshTokenProvider = new SimpleRefreshTokenProvider(),
        AuthenticationMode =  AuthenticationMode.Active
    };

    FacebookAuthOptions = new CustomFacebookAuthenticationOptions();

    app.UseFacebookAuthentication(FacebookAuthOptions);
    app.UseOAuthAuthorizationServer(OAuthServerOptions);

    app.UseOAuthBearerAuthenticationExtended(OAuthBearerOptions);
}

我将尝试将其提交给oAuth中间件的主要分支,这似乎是一个明显的用例,除非我遗漏了什么。

我最近遇到了这个问题。如果用户的访问令牌已过期,我们希望返回一条JSON消息,从而允许消费者web应用程序以静默方式刷新访问令牌并重新发出API请求。我们也不希望依赖抛出的异常来进行令牌生命周期验证

由于不想重新实现任何中间件,我们在JWTBeareAuthenticationOptions中指定了Provider选项,并添加了一个委托来处理OnRequestTokenMethod。委托检查是否可以读取传递给中间件的令牌,如果令牌过期,则在OWIN上下文中设置一个布尔值

app.UseJwtBearerAuthentication(
             new JwtBearerAuthenticationOptions
             {
                 AuthenticationMode = AuthenticationMode.Active,
                 TokenValidationParameters = tokenValidationParameters,                                             
                 Provider = new OAuthBearerAuthenticationProvider
                 {                         
                     OnRequestToken = (ctx) =>
                     {                             
                         if (!string.IsNullOrEmpty(ctx.Token))
                         {
                             JwtSecurityTokenHandler handler = new JwtSecurityTokenHandler();
                             if (handler.CanReadToken(ctx.Token))
                             {
                                 JwtSecurityToken jwtToken = handler.ReadJwtToken(ctx.Token);
                                 
                                 if (jwtToken.IsExpired())
                                     ctx.OwinContext.Set<bool>("expiredToken", true);
                             }                                                                  
                         }

                         return Task.CompletedTask;
                     }
                 }
             });            
我们最终使用中间件检查布尔值的状态:

app.Use((context, next) =>
        {
            bool expiredToken = context.Get<bool>("expiredToken");
            
            if (expiredToken)
            {
                // do stuff
            }

            return next.Invoke();
        });
        app.UseStageMarker(PipelineStage.Authenticate);
app.Use((上下文,下一步)=>
{
bool expiredToken=context.Get(“expiredToken”);
如果(已过期,待续)
{
//做事
}
返回next.Invoke();
});
应用程序UseStageMarker(PipelineStage.Authenticate);

这不是最有效的代码,因为我们在中间件完成后再次解析令牌,还引入了一个新的中间件来处理检查结果,但这是一个全新的视角。

感谢您的回答,但这并不是对我问题的回答。我特别询问如何拒绝无效的访问令牌。我知道Web API本身拒绝401未授权,但我认为这是非常糟糕的风格。如果传递了无效或过期的访问令牌,则响应应该是访问令牌无效或过期的通知,而不是用户未被授权。如果您能删除您的答案,我将非常高兴,因为它没有回答我的问题。我编辑了我的问题,并明确表示我知道这一点,这不是我想要的。很抱歉,你不喜欢这个答案,但这是正确的设计。身份验证与授权是分开的。微软的Dan Roth甚至在最近的构建会议上指出了这一点:我从来没有争论过,我知道身份验证和授权是两件不同的事情。但我只是想用一个错误拒绝无效的身份验证尝试,而不是默默地接受它们。如果我不能对警告做出正确的反应,那么记录警告(就像实现一样)也是毫无意义的。我们需要它来区分过期的、无效的和不充分的许可证。唯一的问题是,授权的其他原因可能是错误的,而不是过期的
public void ConfigureOAuth(IAppBuilder app)
{
    app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);

    OAuthBearerOptions = new OAuthBearerAuthenticationOptions()
    {

    };

    OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions()
    {
        AllowInsecureHttp = true,
        TokenEndpointPath = new PathString("/token"),
        AccessTokenExpireTimeSpan = TimeSpan.FromSeconds(10),

        Provider = new SimpleAuthorizationServerProvider(),
        RefreshTokenProvider = new SimpleRefreshTokenProvider(),
        AuthenticationMode =  AuthenticationMode.Active
    };

    FacebookAuthOptions = new CustomFacebookAuthenticationOptions();

    app.UseFacebookAuthentication(FacebookAuthOptions);
    app.UseOAuthAuthorizationServer(OAuthServerOptions);

    app.UseOAuthBearerAuthenticationExtended(OAuthBearerOptions);
}
app.UseJwtBearerAuthentication(
             new JwtBearerAuthenticationOptions
             {
                 AuthenticationMode = AuthenticationMode.Active,
                 TokenValidationParameters = tokenValidationParameters,                                             
                 Provider = new OAuthBearerAuthenticationProvider
                 {                         
                     OnRequestToken = (ctx) =>
                     {                             
                         if (!string.IsNullOrEmpty(ctx.Token))
                         {
                             JwtSecurityTokenHandler handler = new JwtSecurityTokenHandler();
                             if (handler.CanReadToken(ctx.Token))
                             {
                                 JwtSecurityToken jwtToken = handler.ReadJwtToken(ctx.Token);
                                 
                                 if (jwtToken.IsExpired())
                                     ctx.OwinContext.Set<bool>("expiredToken", true);
                             }                                                                  
                         }

                         return Task.CompletedTask;
                     }
                 }
             });            
    public static class JwtSecurityTokenExtensions
    {        
        public static bool IsExpired (this JwtSecurityToken token)
        {
            if (DateTime.UtcNow > token.ValidTo.ToUniversalTime())
                return true;

            return false;
        }
    }
app.Use((context, next) =>
        {
            bool expiredToken = context.Get<bool>("expiredToken");
            
            if (expiredToken)
            {
                // do stuff
            }

            return next.Invoke();
        });
        app.UseStageMarker(PipelineStage.Authenticate);