如何向已经使用 Identity 的 MVC 控制器添加 API 身份验证和授权?
Posted
技术标签:
【中文标题】如何向已经使用 Identity 的 MVC 控制器添加 API 身份验证和授权?【英文标题】:How to add API authentication and authorization to an MVC Controller already using Identity? 【发布时间】:2021-07-26 15:48:02 【问题描述】:上下文
我有一个带有多个控制器的 ASP.NET Core MVC 应用程序(仅返回视图)。该应用已使用 ASP.NET Core Identity 进行身份验证和授权。但是,有几个地方我需要使用 HTTP 客户端直接调用控制器操作端点作为 API。看来这需要 Identity 似乎不支持开箱即用的 API 身份验证。
问题
我需要使用 component tag helper(渲染模式为 ServerPrerendered
)从嵌入到我的 MVC 视图中的 Razor 组件调用这些控制器操作。
现在,我可以直接从我的 Razor 组件访问我的数据库,方法是将数据库服务注入到我的组件中,就像我对其他 Razor 组件所做的那样。但是,我不能用这个 Razor 组件来做到这一点,因为它需要访问 Identity 的 UserManager
服务并且根据 documentation:
Razor 组件不支持
SignInManager<TUser>
和UserManager<TUser>
。 Blazor 服务器应用使用 ASP.NET Core 标识。
因此,我似乎必须通过注入 IHttpClientFactory
从我的 Razor 组件调用控制器操作。
但是,到目前为止,在我的整个应用程序中,我只需要为两个操作调用控制器操作:
您可能会问为什么我需要 Razor 组件来创建用户帐户。创建帐户是一个相当复杂的步骤(多个步骤),涉及客户端交互,例如将项目动态添加到列表中。 Razor 组件让我变得如此简单。如果我在 .cshtml
视图中这样做,我将不得不使用 javascript/jQuery。
目标
我不认为仅仅为了帮助我完成这两个操作而创建一个完整的 API 项目是值得的(尽管我不介意这样做)。我可以将这些操作添加到我现有的帐户控制器或在同一个项目中创建一个新的 API 控制器。 尽管如此,我仍然必须保护这些端点。
主要问题
如何向这两个控制器操作(创建用户和编辑用户)添加 API 身份验证和授权,而不影响现有的 ASP.NET Core 身份设置正在执行的操作?
我仍然希望我的应用程序中的其余控制器使用相同的身份验证/授权,但这些 Create
和 Edit
用户操作的 API 身份验证/授权。
或者由于我的 Razor 组件嵌入在 MVC 视图中,有没有办法通过我的组件调用控制器操作,就像 MVC 视图通过发送带有请求的 cookie 一样?
无论如何,我的目标是保护两个控制器操作端点。
代码
这是Startup.cs
中当前配置身份的方式:
services.AddDefaultIdentity<AppUser>()
.AddRoles<IdentityRole>()
.AddEntityFrameworkStores<AppDbContext>();
这是我的Create
控制器操作的简化版本:
[Authorize(Roles = "Administrator")]
public async Task<IActionResult> Create(UserViewModel model)
if (ModelState.IsValid)
AppUser user = model.ToAppUser(); // Method on view model does the mapping
user.Id = Guid.NewGuid().ToString();
IdentityResult createUserResult = await _userManager.CreateAsync(user, model.Password);
if (createUserResult.Succeeded)
await userManager.AddToRoleAsync(user, role.Name);
return Ok();
foreach(var error in createUserResult.Errors)
ModelState.AddModelError("", error.Description);
这就是我从 Razor 组件调用它的方式:
CreateUser.razor
:
@inject IHttpClientFactory HttpClientFactory
<EditForm Model="_model" OnValidSubmit="HandleValidSubmit">
...
</EditForm>
@code
private UserViewModel _model = new();
public async Task HandleValidSubmit()
var modelAsJson = new StringContent(
JsonSerializer.Serialize(_model),
Encoding.UTF8,
"application/json");
using var httpResponse =
await _httpClient.PostAsync("/api/Accounts/Create", modelAsJson);
//...
使用 <component>
将组件嵌入到 MVC 视图中:
Create.cshtml
...
<component type="typeof(CreateUser)" render-mode="ServerPrerendered" />
更新
经过一些研究,我发现 JWT 是最常用的 API 身份验证方式。根据here 的说明,这是在Startup.cs
中添加的方式:
services.AddAuthentication( auth =>
auth.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
auth.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
)
.AddJwtBearer(options => ..)
这里,当默认身份验证方案设置为 JWT 时,这会覆盖我的默认身份设置吗?如何同时使用这两种方案?
【问题讨论】:
创建一个 API。如果不是,那么调用该控制器的用户必须在点击控制器之前通过身份登录。您必须使用 OIDC 客户端在客户端实现某种类型的静默登录,这对我来说比连接 API 更令人头疼。更不用说您将带来的安全漏洞,例如跨站点脚本。 【参考方案1】:有一种方法可以在 asp.net core 中支持多种身份验证方案。
看起来像这样:
启动类:
public void ConfigureServices(IServiceCollection services)
serviced.AddDefaultIdentity<AppUser>()
.AddRoles<IdentityUser>()
.AddEntityFrameworkStores<AppDbContext>();
services.AddAuthentication()
.AddCookie(options =>
options.LoginPath = "/Account/Unathorized/"
options.AddAccessDeniedPath = "/Account/Forbidden"
);
控制器:
[Authorize(AuthenticationSchemes = YourCustomeScheme)]
public class AuthController: controller
您可以根据需要添加和使用任意数量的方案。
【讨论】:
AuthenticationSchemes
对于这两种方案的值是多少?这些方案是自动创建的吗?
您可以决定使用哪种方案,一旦添加了身份验证,您就可以完全控制。无论是 1 还是 100。都相互独立。【参考方案2】:
由于您使用的是 MVC 控制器,因此您可以使用 cookie 身份验证而不是承载令牌 (JWT),后者主要用于 api 控制器(即使两者都可以使用)。
对于 cookie,您可以使用 [Authorize(AuthenticationSchemes = CookieAuthenticationDefaults.AuthenticationScheme)]
对于控制器上方的 jwt [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
(使用时您需要
Microsoft.AspNetCore.Authentication.Cookies
)。
该方案只不过是一个与您的 Startup.cs 中定义的字符串匹配的字符串,例如:
services.AddAuthentication(options =>
options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = CookieAuthenticationDefaults.AuthenticationScheme;
)
.AddCookie(options =>
options.AccessDeniedPath = "/AccessDenied";
options.ExpireTimeSpan = TimeSpan.FromHours(9);
options.Cookie = new CookieBuilder()
Name = "cookie",
SameSite = SameSiteMode.Strict,
;
options.LoginPath = "/Login";
....
但是,我从问题中不明白,身份验证是如何处理的,即谁颁发令牌或 cookie。形成您共享的link,这将是“使用 Web API 端点对用户进行身份验证”部分。或者,您可能会选择 Identity Server,或者第三个选项。
附言身份验证/授权是硬性功能 - 保持冷静并继续编码:)
【讨论】:
Cookie 身份验证听起来也不错。我选择了 JWT,因为这是我看到的大多数来源所涵盖的内容。另外,我打算从同一个应用程序中发行代币。是的,身份验证/授权仍然相当复杂,希望它变得更好,谢谢:) 另外,我仍然没有弄清楚如果我使用默认的 ASP.NET Core 标识,方案的价值是什么。 @AmalK 没有默认值(据我所知)。您必须在services.AddAuthentication
中指定它
我目前有默认的身份 UI,我仍然希望将其用于我的 MVC 控制器返回视图。添加使用:services.AddDefaultIdentity<AppUser>().AddRoles<IdentityRole>().AddEntityFrameworkStores<AppDbContext>();
我知道这是一个扩展方法,内部调用AddAuthentication()
。
@AmalK 使用 cookie。你可以看看实现代码(它是开源的)github.com/dotnet/aspnetcore/blob/…以上是关于如何向已经使用 Identity 的 MVC 控制器添加 API 身份验证和授权?的主要内容,如果未能解决你的问题,请参考以下文章
如何在非控制器类中从 Microsoft.AspNetCore.Identity 获取 UserID
asp.net identity 3.0.0 在MVC下的基本使用
FluentValidation/MVC/ASP Identity:验证模型时如何获取当前用户 ID?
在 Asp.net Identity MVC 5 中创建角色
asp.net identity 3.0.0 在MVC下的基本使用 序言
如何与 Windows 服务应用程序共享 MVC 5 Identity 2.0 身份验证(尤其是 SignInManager)?