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

8 浏览
0 Comments

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

在一个严格的(即无视图的)ASP.NET Core WebAPI项目中,我在使用[Authorize]注解时遇到了麻烦,因为我无法保证客户端会使用什么平台。也就是说,这个应用程序需要是一个真正的API,不需要特定的平台来访问。\n

\n注意:当我说“严格的WebAPI”时,我的项目实际上是从以下命令生成的MVC项目...\n

dotnet new mvc --auth Individual

\n...然后我立即删除了所有的视图等内容,并更改了路由设置以符合WebAPI的约定。\n

\n


\n

我正在尝试的

\n当我通过AJAX访问一个标准的登录函数(如下所示,只保留了必要的部分)时,我会得到一个JSON负载和一个cookie的返回。\n

[HttpPost("apiTest")]
[AllowAnonymous]
public async Task 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);
}

\n成功后,会返回一个类似以下的cookie:\n

.AspNetCore.Identity.Application=CfDJ8Ge9E-[已删除多个字符]; path=/; domain=localhost; HttpOnly; Expires=Fri, 16 Mar 2018 16:27:47 GMT;

\n


\n

问题

\n在一个看起来成功登录后,我尝试访问两个完全相同的API端点,一个使用AllowAnonymous注解,另一个使用Authorized注解:\n

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();
}

\nanonymousTest端点在登录前后都可以访问,尽管它告诉我我没有登录(SignedInfalse),甚至在登录后也是如此。而authorizedTest端点则永远无法访问。\n我猜测单个cookie不足以通过[Authorized]标签。我相信我还需要一个防伪值,可以从由@Html.AntiForgeryToken()生成的表单中的隐藏值获取,或者从View似乎默认发送的第二个cookie中获取。那个cookie看起来像这样...\n.AspNetCore.Antiforgery.0g4CU0eoNew=CfDJ8Ge9E-[已删除多个字符]; path=/; domain=localhost; HttpOnly; Expires=Tue, 19 Jan 2038 03:14:07 GMT;\n\n


\n

失败的解决方案

\n我看到很多关于如何使用纯AJAX的答案,基本上都说“从隐藏表单中获取防伪信息”或者“从头部读取防伪信息”,但我没有View;没有隐藏的表单。我也不想为了让客户端获取而弄乱发送一个部分视图。\n我看到的最好的答案是这个关于使用iOS的解决方案。情况似乎是类似的:\n

\n因为我们不能向客户端传递HTML,所以我们不能使用标准的@Html.AntiForgeryToken(),因此我们必须使用AntiForgery.GetTokens来获取和分发令牌给我们的客户端。\n

\n但是,即使我的Intellisense“看到”AntiForgery.GetTokens,它也无法编译,即使我已经获取了似乎是正确的nuget包:\n

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

\n


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


\n

为什么我认为这是一个防伪问题

\n有一段时间,我无法理解为什么我的代码在一个严格的WebAPI项目中无法工作,但在标准的ASP.NET Core项目(从dotnet new mvc --auth Individual创建的项目)的AccountController中可以工作。\n关键是options.LoginPath。当我在WebAPI中时,我会转发到一个API端点来登录:\noptions.LoginPath = \"/api/accounts/requestLogin\";\n在标准项目中,默认情况下是一个View,它在加载时提供了防伪cookie:\noptions.LoginPath = \"/Account/Login\"; // 一旦Login.cshtml加载完毕,就有了.AspNetCore.Antiforgery cookie。\n奇怪的是,有时我可以删除第二个AspNetCore.Antiforgerycookie,仍然可以访问一个[Authorize]注解的方法,所以我不能100%确定我是否在错误的方向上努力,但这是迄今为止我找到的最好的线索...

0
0 Comments

问题的出现:

在传统的网站中,使用基于cookie的身份验证来授权请求,浏览器会自动将所有的cookie随请求一起发送到服务器,无需用户干预。但是在API中,身份验证需要手动添加,通常通过Authorization请求头来处理,其中包含类似于令牌的内容。如果请求中没有传递类似于令牌的内容,服务器会将其视为未经身份验证的请求。API无法唯一标识特定请求的来源,也不知道或关心特定客户端之前发出了什么请求。HTTP协议的设计是分散的。

解决方法:

1. 身份验证:API中的身份验证需要手动添加到请求的Authorization请求头中,以授权请求。如果没有传递身份验证信息,服务器将拒绝请求。

2. 防止XSRF攻击:使用防伪令牌来防止恶意网站将用户的信息发送到其他站点。防伪令牌是加密的,无法在恶意网站上成功验证,验证失败时请求会被拒绝。API通常用于第三方使用,防伪令牌与API的兼容性较低。

Identity和角色在WebAPI应用中的替代方案:

- 使用IdentityServer4来替代Identity,它可以与Identity集成,并处理复杂的身份验证工作流程,如OAuth。

- 可以自定义控制器操作来检查请求头中的合法身份验证令牌,并在Identity登录期间颁发这些令牌。但这只是将Identity简化为基本的身份验证令牌。

ASP.NET Core对于令牌身份验证的支持:

ASP.NET Core原生支持令牌身份验证,但它对于如何实现令牌身份验证保持中立。因此,仍然需要处理获取令牌的过程,可以使用第三方库如OpenIddict、IdentityServer等来实现。这些库得到了Microsoft的支持和使用。

结论:

在API中,需要手动添加身份验证信息到请求头中进行授权。防伪令牌用于防止XSRF攻击,但与API的兼容性较低。可以使用IdentityServer4等第三方库来实现复杂的身份验证工作流程。ASP.NET Core原生支持令牌身份验证,但具体如何实现令牌身份验证需要自行处理。

0