所以我一直为这个问题problem头。
我们有一个Web应用程序正在使用 IdentityServer4 和 AspNetIdentity 进行身份验证和注册用户(这已按预期工作)。 此外,我们还有另一个API(在同一解决方案内部),该API能够使用IdentityServer4来验证访问该API的用户的身份。
但是,问题是,除了身份验证之外,我们不能使用API 创建新用户。
例如,用户应该不仅可以通过Web应用程序,还可以通过Web API创建其他用户,因为在本例中,用户已链接到其他用户(将其视为多个配置文件)。
我不是很熟悉.Net Core框架附带的所有配置服务,并且我尝试了多种通过API访问Web应用程序用户管理器的方法,以通过经典的POST请求注册用户,但是似乎没有工作。在线搜索非常棘手,因为我们的问题非常具体,这就是我在这里发布的原因。
API Startup.cs-ConfigureServices:
services.AddAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme)
.AddIdentityServerAuthentication(options =>
{
// base-address of your identityserver
options.Authority = Configuration["IdentityServer:Url"];
// name of the API resource
options.ApiName = Configuration["IdentityServer:APIName"];
options.ApiSecret = Configuration["IdentityServer:APISecret"];
options.EnableCaching = true;
options.CacheDuration = TimeSpan.FromMinutes(10); // that's the default
options.RequireHttpsMetadata = Convert.ToBoolean(Configuration["IdentityServer:RequireHttpsMetadata"]);
});
API Startup.cs-配置:
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseCors("AllowAllOrigins");
app.UseAuthentication();
app.UseMvc();
}
API UsersController.cs-构造函数:
private readonly UserManager<ApplicationUser> _userManager;
private readonly ApplicationDbContext _context;
public UsersController(IUserService service,
ApplicationDbContext context,
UserManager<ApplicationUser> userManager)
{
_service = service;
_userManager = userManager;
_context = context;
}
现在的问题是,当我启动API并尝试访问UsersController时,出现以下错误:
System.InvalidOperationException: Unable to resolve service for type 'Microsoft.AspNetCore.Identity.UserManager`1[XXXXX.Data.Models.ApplicationUser]' while attempting to activate 'XXXXXXX.Api.Controllers.UsersController'.
我衷心希望我至少能找到一些建议,以便进行。
如果有不清楚的答复,请提供我更多的信息或使事情更清楚。
亲切的问候,
Marios。
编辑: 谢谢大家的答复。 @Vidmantas在下面提供的代码段可以解决问题。
由于对.net核心的了解有限,因此我在配置服务功能中进行了很多次尝试和错误,如您所想,这是行不通的。我坚信使用.net内核很容易(例如API),但是在配置服务时,复杂性(大多令人困惑/困惑)会激增。
对于体系结构,您给我提供了一些关于将来重构的好主意。记录下来。
Marios。
答案 0 :(得分:3)
如果我理解正确的话,那么你是不是真的应该创建通过API的用户 - 这就是为什么你的Identity Server 4的地方 - 为您的用户群提供认证中央权威。你真正需要的是什么:
如果您选择了最后一个选项,则可能需要添加以下内容:
services.AddDbContext<IdentityContext>(); //make sure it's same database as IdentityServer4
services.AddIdentityCore<ApplicationUser>(options => { });
new IdentityBuilder(typeof(ApplicationUser), typeof(IdentityRole), services)
.AddRoleManager<RoleManager<IdentityRole>>()
.AddSignInManager<SignInManager<ApplicationUser>>()
.AddEntityFrameworkStores<IdentityContext>();
这将为您提供足够的服务来使用UserManager
,并且不会设置任何不必要的身份验证方案。
由于关注点分离,我不建议使用最后一种方法-您的API应该关注提供资源,不创建用户和提供资源。我认为第一种和第二种方法都不错,但是我总是倾向于为AspNetIdentity管理提供干净的单独服务。
我在其中一个项目中实现了这种方法的示例架构:
答案 1 :(得分:2)
我和你有类似的情况。
我们的用例是通常将通过身份服务器创建新用户。但是,我们还希望该应用程序能够邀请用户。这样我就可以登录该应用程序了,我想邀请我的朋友。想法是邀请的行为与用户创建自己的行为相同。
因此它将向我的朋友发送一封带有附加代码的电子邮件,然后用户可以提供密码并拥有一个帐户。
为此,我在帐户控制器上创建了一个新操作。
[HttpGet]
[AllowAnonymous]
public async Task<IActionResult> Invited([FromQuery] InviteUserRequest request)
{
if (request.Code == null)
{
RedirectToAction(nameof(Login));
}
var user = await _userManager.FindByIdAsync(request.UserId.ToString());
if (user == null)
{
return View("Error");
}
var validateCode = await _userManager.VerifyUserTokenAsync(user, _userManager.Options.Tokens.PasswordResetTokenProvider, "ResetPassword", Uri.UnescapeDataString(request.Code));
if (!validateCode)
{
return RedirectToAction(nameof(Login), new { message = ManageMessageId.PasswordResetFailedError, messageAttachment = "Invalid code." });
}
await _userManager.EnsureEmailConfirmedAsync(user);
await _userManager.EnsureLegacyNotSetAsync(user);
return View(new InvitedViewModel { Error = string.Empty, Email = user.Email, Code = request.Code, UserId = user.Id });
}
当用户接受电子邮件时,我们将其添加。
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Invited([FromForm] InvitedViewModel model)
{
if (!ModelState.IsValid)
{
model.Error = "invalid model";
return View(model);
}
if (!model.Password.Equals(model.ConfirmPassword))
{
model.Error = "Passwords must match";
return View(model);
}
if (model.Terms != null && !model.Terms.All(t => t.Accept))
{
return View(model);
}
var user = await _userManager.FindByEmailAsync(model.Email);
if (user == null)
{
// Don't reveal that the user does not exist
return RedirectToAction(nameof(Login), new { message = ManageMessageId.InvitedFailedError, messageAttachment = "User Not invited please invite user again." });
}
var result = await _userManager.ResetPasswordAsync(user, Uri.UnescapeDataString(model.Code), model.Password);
if (result.Succeeded)
{
return Redirect(_settings.Settings.XenaPath);
}
var errors = AddErrors(result);
return RedirectToAction(nameof(Login), new { message = ManageMessageId.InvitedFailedError, messageAttachment = errors });
}
之所以这样做,是因为只有身份服务器才应读写其数据库。 api和第三方应用程序永远不需要直接更改由另一个应用程序控制的数据库。因此,API通过这种方式告诉身份服务器邀请用户,然后身份服务器控制其他所有内容。
通过这种方式也可以消除您在API中添加用户管理器的需要:)
答案 2 :(得分:2)
我不建议您在不同API之间使用共享数据库。 如果您需要使用其他API扩展Identity Server 4,则可以将LocalApiAuthentication用于控制器。