如何在无视图的WebAPI ASP.NET Core应用程序中使用[Authorize]和antiforgery?

问题描述 投票:0回答:1

当我不能保证客户端将使用什么平台时,我在严格(即[Authorize]less)的ASP.NET Core WebAPI项目中使用View注释时遇到了麻烦。也就是说,应用程序需要是一个真正的API,不需要特定的平台来访问。

注意:当我说“严格的WebAPI”时,我的项目实际上是作为由...生成的MVC项目开始的。

dotnet new mvc --auth Individual

...我立即从中删除了所有视图等,并更改了路由首选项以匹配WebAPI约定。


What I'm trying

当我通过AJAX访问标准登录功能(在下面的粘贴中剥离到基本要素)时,我获得了一个JSON有效负载并返回了一个cookie。

[HttpPost("apiTest")]
[AllowAnonymous]
public async Task<IActionResult> ApiLoginTest([FromBody] LoginViewModel model, string returnUrl = null)
{
    object ret = new { Error = "Generic Error" };

    if (ModelState.IsValid)
    {
        var result = await _signInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, lockoutOnFailure: false);
        if (result.Succeeded)
            ret = new { Success = true };
        else
            ret = new { Error = "Invalid login attempt" };
    }

    return new ObjectResult(ret);
}

成功时,返回类似于以下内容的cookie:

.AspNetCore.Identity.Application=CfDJ8Ge9E-[many characters removed]; path=/; domain=localhost; HttpOnly; Expires=Fri, 16 Mar 2018 16:27:47 GMT;

Issue

在看似成功登录后,我尝试访问两个完全相同的API端点,一个注释AllowAnonymous和一个Authorized

private IActionResult _getStatus()
{
    object ret = new { Error = "Generic Error" };

    var isSignedIn = _signInManager.IsSignedIn(User);
    var userName = _userManager.GetUserName(User);

    return new ObjectResult(
        new {
            SignedIn = isSignedIn,
            Name = userName
        }
    );
}

[HttpGet("authorizedTest")]
[Authorize]
public IActionResult GetCurrentLoginInfo2()
{
    return _getStatus();
}

[HttpGet("anonymousTest")]
[AllowAnonymous]
public IActionResult GetCurrentLoginInfo()
{
    return _getStatus();
}

登录前后可以访问anonymousTest端点,但它告诉我即使登录后我还没有登录(SignedInfalse)。 authorizedTest端点永远不可访问。

我的猜测是单个cookie不足以通过[Authorized]标签。我相信我还需要一个防伪值,要么是来自@Html.AntiForgeryToken()生成的表单中的隐藏值,要么是Views默认发送的第二个cookie。那饼干看起来像这样......

.AspNetCore.Antiforgery.0g4CU0eoNew=CfDJ8Ge9E-[many characters removed]; path=/; domain=localhost; HttpOnly; Expires=Tue, 19 Jan 2038 03:14:07 GMT;

Failed solutions

我已经看到很多关于如何使用纯AJAX的答案,基本上说“get the anti-forgery from the hidden form”或“read it from the headers”,但我没有View;没有隐藏的形式。我也不想真正想要kazdge sending down a partial view for the clients to scrape

我见过的最好的答案是this one talking about using iOS。情况似乎是类似的:

因为我们不向客户提供HTML,所以我们不能使用标准的@Html.AntiForgeryToken(),因此我们必须使用AntiForgery.GetTokens来获取并将令牌分发给我们的客户。

但即使我的Intellisense“看到”AntiForgery.GetTokens,它也无法编译,即使在抓住了似乎正确的nuget包之后:

dotnet add package microsoft-web-helpers --version 2.1.20710.2

那么我的问题是:如何在Razor之外使用Antiforgery,但使用ASP.NET Core创建一个受Identity限制的WebAPI风格的项目?


Why I think it's an anti-forgery issue

有一段时间,我无法弄清楚为什么我的代码在严格的WebAPI项目中不起作用,但是当移植到标准身份ASP.NET Core项目(一个从dotnet new mvc --auth Individual创建)AccountController时。

钥匙? options.LoginPath。当我在WebAPI的土地上时,我转发到API端点登录:

options.LoginPath = "/api/accounts/requestLogin";

在库存项目中,默认情况下它是View,它在加载时提供防伪cookie:

options.LoginPath = "/Account/Login"; // As soon as Login.cshtml loads, BAM. .AspNetCore.Antiforgery cookie.

奇怪的是,有时候我可以删除Postman中的第二个AspNetCore.Antiforgery cookie,并且仍然可以访问[Authorize]-annotated方法,所以我不是100%肯定我不是在这里咆哮错误的树,但这是我迄今为止最好的领导。 ..

c# ajax asp.net-web-api asp.net-core-2.0 antiforgerytoken
1个回答
6
投票

首先,请求是幂等的:每个请求都是唯一的,必须包含服务请求所需的所有信息,包括授权。传统网站使用基于cookie的身份验证,并且Web浏览器一致地将所有cookie发送回服务器,无需用户干预。随请求一起发送的cookie用于授权请求,给出用户经过身份验证的外观,而无需不断传递用户名和密码。

API是一种不同的野兽。由于它通常不是发出请求的实际Web浏览器,因此添加授权是一种手动操作。这通常通过Authorization请求标头处理,其中包括类似于承载令牌的内容。如果你没有通过你的请求传递这样的东西,那么它就像用户从未进行过身份验证一样,因为实际上他们没有进行身份验证。服务器不能唯一地标识特定请求来自何处,并且不知道或不关心特定客户端之前发出请求。 HTTP协议按设计分散。

其次,防伪代币是完全不同的东西。它们旨在防止XSRF攻击,其中恶意网站可能会尝试从您的网站托管表单的副本以捕获信息,但随后发布到您的网站,以便用户不会意识到发生了任何事情。例如,假设我在http://faceboook.com设置了一个站点(注意额外的o)。大多数用户不会注意到链接中的这种细微差别,甚至可能会在地址栏中意外地将其自行键入。然后,我的恶意网站会创建一个真正的Facebook主页的副本,并有一个登录的地方。用户将他们的Facebook登录凭据输入我的表单(我稍后会使用它来窃取他们的帐户),但我会将这些凭据发布到真实Facebook网站使用的同一个处理程序。最终结果是用户最终在Facebook,登录,并继续他们发布猫视频等业务。但是,现在我有他们的登录凭据。

防伪代币可以防止这种情况发生。令牌已加密,因此我无法在恶意网站上创建能够在Facebook端成功验证的令牌。验证失败时,请求被拒绝。基于存在要解决的防伪令牌的问题,它们大多与API不兼容,因为通常API旨在由第三方使用。受防伪令牌保护的任何API端点都无法被该应用或域外部的任何内容使用。无论如何,它们与请求的身份验证/授权无关。

© www.soinside.com 2019 - 2024. All rights reserved.