【问题标题】:.NET core returning 500 instead of 401 when missing cookie.NET 核心在缺少 cookie 时返回 500 而不是 401
【发布时间】:2017-09-25 12:22:43
【问题描述】:

我有一个使用 cookie 身份验证的 .NET 核心 API。它由具有自己的登录路径的 PWA/SPA 访问。

在Startup.cs:

public void ConfigureServices(IServiceCollection services)
{
...
    services.AddIdentity<MyUser, MyRole>(options =>
    {
        ...

        // Use cookie authentication
        var expiresIn = new TimeSpan(1, 0, 0); // 1 hour timeout
        var c = options.Cookies.ApplicationCookie;
        c.AuthenticationScheme = "appSchemeName";
        c.CookieName = "appCookieName";
        c.AutomaticAuthenticate = true;

        // If this is true auth failures become redirects
        c.AutomaticChallenge = false;
        c.SlidingExpiration = true;
        c.ExpireTimeSpan = expiresIn;

        // Store sessions in the cache with the same TTL as the cookie
        c.SessionStore = new MyRedisSessionStore(expiresIn);
    });
    ...
}

public void Configure(...) 
{
    ...
    app.UseIdentity();
    ...
    app.UseMvc();
}

在我的客户端 JS 中,当身份验证 cookie 无效或丢失时,我希望出现 401,并在这种情况下显示登录表单。

但是,当没有有效 cookie 的用户访问标有 [Authorize] 的控制器时,他们会收到 500 状态错误:

InvalidOperationException:没有配置身份验证处理程序来处理方案:自动

如果我更改 c.AutomaticChallenge = true;,那么我会收到一个 302 重定向到 {site}/Account/Login?ReturnUrl={api resource it was trying to load}。这很奇怪,因为那不是有效的路线,而且我没有设置它。

如何解决此问题,以便未经身份验证的用户在服务器上获得 401 而不是 500 异常。

我意识到我可以覆盖它并使用自定义响应编写自己的身份验证,但必须有办法让内置的 [Authorize] 返回正确的 HTTP 状态代码。

【问题讨论】:

  • 您是否尝试过在运行时一次单步执行代码以查看它在哪一行中断?
  • @sam 微软的中间件在它到达我的代码之前就抛出了错误。

标签: .net authentication asp.net-web-api asp.net-core http-status-code-401


【解决方案1】:

我对此有一个修复,因为我有可以工作的代码,但这太可怕了,不可能是处理这个问题的最佳方法。

我当前的代码给我一个 500 服务器错误(AutomaticChallenge = false 时)或 302 重定向(AutomaticChallenge = true 时)。两者在我的 SPA 中都没用,因为 500 太通用了,302 透明地重定向到不存在的页面,我在客户端 JS 中得到 404。

我的解决方法是使用 302,然后覆盖 Response:

// Make ASP.NET give us the 302 redirect when cookie is missing/broke
c.AutomaticChallenge = true;

c.Events = new CookieAuthenticationEvents
{
    // Override the 302 redirection with the 401 we actually want 
    OnRedirectToLogin = context =>
    {
        context.Response.StatusCode = 401;
        return Task.FromResult(0); ;
    }
};

这行得通,因此它可以回答这个问题,但它是对中间件的一种可怕的破解,它的行为不符合我的要求。

【讨论】:

  • 我只想要一个简单的,401 表示未经身份验证,403 表示未经授权,因此我的水疗中心可以显示正确的信息。我会暂时使用它,直到找到更好的解决方案。谢谢!
【解决方案2】:

我在使用 Postman 测试端点时遇到了这个确切的问题。

如果您查看source code 的OnRedirectToLogin 事件,您可以看到它正在检查请求是否为AJAX 请求。要使IsAjaxRequest 返回true,X-Requested-With 标头需要具有XMLHttpRequest 作为它的值。

如果您碰巧使用 Postman 进行测试,则必须手动应用 X-Requested-With 以及正确的值:

最后,如果您没有其他需要设置的特殊配置,您的CookieAuthenticationOptions 应该是这样的:

app.UseCookieAuthentication(new CookieAuthenticationOptions
{
    AutomaticAuthenticate = true,
    AutomaticChallenge = true
});

【讨论】:

  • 有趣!我正在使用fetch,它是XMLHttpRequest 的替代品。较旧的技术仅用作垫片。我可以添加自定义标头,但如果是这种情况,那么 .NET 的处理将是一个非常严重的问题。
  • @Keith 嗯,我不会说这是一个严重的问题。当您向端点发出 ajax 请求时,您不希望在响应中获取登录页面的标记或 404,对吗?肯定可以更好地记录下来。
  • 是的,但这不是 REST API 的工作。它严格来说是一个只返回 JSON 的 API,它永远不应该返回 302(不管是什么发出请求),因为没有提供登录 HTML 页面。服务器不应猜测是谁发出请求并决定重定向它们,因为它认为请求来自用户(而不是 AJAX 请求)。
猜你喜欢
  • 2020-05-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-09-08
  • 2011-09-30
  • 1970-01-01
  • 2017-09-12
  • 2020-01-13
相关资源
最近更新 更多