【发布时间】:2018-08-11 00:34:33
【问题描述】:
当我无法保证客户端将使用什么平台时,我无法在严格的(即Viewless)ASP.NET Core WebAPI 项目中使用[Authorize] 注释。也就是说,应用需要是真正的 API,不需要特定平台即可访问。
注意:当我说“严格的 WebAPI”时,我的项目实际上是作为由...生成的 MVC 项目开始的。
dotnet new mvc --auth Individual...我立即从中删除了所有视图等,并更改了路由首选项以匹配 WebAPI 约定。
我正在尝试什么
当我通过 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;
问题
在看似成功的登录之后,我尝试访问两个执行完全相同操作的 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 端点在登录前后都可以访问,尽管它告诉我即使在登录后我也没有登录(SignedIn 是false)。 authorizedTest 端点永远无法访问。
我的猜测是单个 cookie 不足以通过 [Authorized] 标记。我相信我还需要一个防伪值,要么来自@Html.AntiForgeryToken() 生成的表单中的隐藏值,要么来自Views 默认发送的第二个cookie。那个cookie看起来像这样......
.AspNetCore.Antiforgery.0g4CU0eoNew=CfDJ8Ge9E-[many characters removed]; path=/; domain=localhost; HttpOnly; Expires=Tue, 19 Jan 2038 03:14:07 GMT;
失败的解决方案
我已经看到很多关于如何使用纯 AJAX 的答案,基本上都是“get the anti-forgery from the hidden form”或“read it from the headers”,但我没有View;没有隐藏的形式。我也不想混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 创建身份受限的 WebAPI 样式项目?
为什么我认为这是一个防伪问题
有一段时间,我想不通为什么我的代码不能在严格的 WebAPI 项目中运行,但在移植到标准标识 ASP.NET Core 项目(从 dotnet new mvc --auth Individual 创建的)AccountController 时却能运行.
钥匙? options.LoginPath。当我在 WebAPI 领域时,我转发到 一个 API 端点 以登录:
options.LoginPath = "/api/accounts/requestLogin";
在 stock 项目中,默认为 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