如何在 ASP.NET Core 2.2 中实现标识

Posted

技术标签:

【中文标题】如何在 ASP.NET Core 2.2 中实现标识【英文标题】:How to implement Identity in ASP.NET Core 2.2 【发布时间】:2019-06-25 11:45:21 【问题描述】:

在我的网站中,我想让管理员使用帐户登录以修改数据库。另外,我将来可能会添加一些角色和帐户。

所以,我正在尝试实现 identity

我在 Visual Studio 2019 中创建了一个项目(不确定这是否重要)并选择了带有 MVC 的 ASP.NET Core 2.2(我自己安装了 Core 2.2),使用个人用户帐户进行身份验证,然后我点击了哪个框说:“为 HTTPS 配置”。

项目制作完成后,我打开它,随机注册一个帐户,出现错误,然后迁移数据库进行修复。

然后,我添加appsetting.json

"UserSettings": 
    "UserName": "MyAdminUser",
    "UserEmail": "MyAdminUser@gmail.com",
    "UserPassword": "A_123456a"

在文件夹Data中创建一个新类:

using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;

namespace WebApplication1.Data 
    public static class Seed 
        public static async Task CreateRoles(IServiceProvider serviceProvider, IConfiguration Configuration) 
            // Add customs roles
            var RoleManager = serviceProvider.GetRequiredService<RoleManager<IdentityRole>>();
            // Note: I used IdentityUser instead of make my ApplicationUser as other people do because the default idendity is fine for me, I don't need additional fields nor I want to make this more difficult.
            var UserManager = serviceProvider.GetRequiredService<UserManager<IdentityUser>>();
            string[] roleNames =  "Admin" ;
            IdentityResult roleResult;

            foreach (var roleName in roleNames) 
                // Create roles and seeding them to the database
                var roleExist = await RoleManager.RoleExistsAsync(roleName);
                if (!roleExist) 
                    roleResult = await RoleManager.CreateAsync(new IdentityRole(roleName));
                
            

            // Create a super user
            var poweruser = new IdentityUser 
                UserName = Configuration.GetSection("AppSettings")["UserSettings:UserName"],
                Email = Configuration.GetSection("AppSettings")["UserSettings:UserEmail"]
            ;

            string userPassword = Configuration.GetSection("AppSettings")["UserSettings:UserPassword"];
            var user = await UserManager.FindByEmailAsync(Configuration.GetSection("AppSettings")["UserSettings:UserEmail"]);

            if (user == null) 
                var createPowerUser = await UserManager.CreateAsync(poweruser, userPassword);
                if (createPowerUser.Succeeded) 
                    // Assign the new user the "Admin" role 
                    await UserManager.AddToRoleAsync(poweruser, "Admin");
                
            
        
    

最后将Program类的Main方法替换为:

public static void Main(string[] args) 
    var host = CreateWebHostBuilder(args).Build();    
    using (var scope = host.Services.CreateScope()) 
        var services = scope.ServiceProvider;
        var serviceProvider = services.GetRequiredService<IServiceProvider>();
        var configuration = services.GetRequiredService<IConfiguration>();
        Seed.CreateRoles(serviceProvider, configuration).Wait();
        
    host.Run();

但是当我运行它时,网页上出现错误HTTP Error 500.30 - ANCM In-Process Start Failure,并且调试控制台显示以下异常:

'System.InvalidOperationException' in Microsoft.Extensions.DependencyInjection.Abstractions.dll 'System.AggregateException' in System.Private.CoreLib.dll

我不知道如何解决这个问题。

另外,我发现了所有这些问题(及其答案)1、2、3、4、5、6 以及这些非 SO 7 和 @987654328 @。但是我对它们的主要问题是每个人都使用不同的方式来实现身份(其中一些已经过时到 2.2)而且我不知道哪个更好,甚至在我的项目中出现一些错误或者我不明白完全没有,所以我尝试这样做(我读到在Program 中执行此代码的性能优于Startup)。

顺便说一句,我还尝试从Programm 中删除我的代码,而是在StartupConfigure 方法中添加参数IServiceProvider serviceProvider 和:

// I used _ = because VS said me something about async
_ = Seed.CreateRoles(serviceProvider, Configuration);

【问题讨论】:

【参考方案1】:

问题是您试图从IServiceProvider 检索范围内的服务而不创建范围。有趣的是,您在对Seed.CreateRoles 的调用周围创建了一个作用域,但随后传入了IServiceProvider,它没有作用域。

您应该做的是传入IServiceProvider 并在您的Seed.CreateRoles 方法中创建范围inside,或者将范围创建留在现在的位置,而是传入您的范围服务,即RoleManagerUserManager

编辑

您需要执行以下操作之一:

    注入IServiceProvider,然后在种子方法中创建你的作用域:

    var host = CreateWebHostBuilder(args).Build();
    Seed.CreateRoles(host.Services);
    host.Run();
    

    然后:

    public static async Task CreateRoles(IServiceProvider services) 
        var configuration = services.GetRequiredService<IConfiguration>();
        using (var scope = services.CreateScope())
        
            var roleManager = scope.ServiceProvider.GetRequiredService<RoleManager<IdentityRole>>();
            var userManager = scope.ServiceProvider.GetRequiredService<UserManager<IdentityUser>>();
    
            // seed data
        
    
    

    注入您的作用域服务:

    var host = CreateWebHostBuilder(args).Build();
    
    var configuration = host.Services.GetRequiredService<IConfiguration>();
    using (var scope = host.Services.CreateScope())
    
        var roleManager = scope.ServiceProvider.GetRequiredService<RoleManager<IdentityRole>>();
        var userManager = scope.ServiceProvider.GetRequiredService<UserManager<IdentityUser>>();
        Seed.CreateRoles(configuration, roleManager, userManager);
    
    host.Run();
    

【讨论】:

对不起,我想我没听懂你的意思。那么,我是否应该将services 变量作为参数传递,然后在Seed.CreateRoles 内部只有参数IServiceProvider serviceProvider 并执行var Configuration = serviceProvider.GetRequiredService&lt;IConfiguration&gt;();?这并不能解决错误。我在互联网上发现了关于示波器的信息,并认为它是正确的。 见上面的编辑。本质上,必须在同一个 IServiceProvider 实例创建的范围内使用您的范围服务。如果您在范围内调用种子方法并不重要,如果您仍然在内部直接使用IServiceProviderIServiceProvider 没有作用域,因此如果不创建另一个作用域,就无法从中提取作用域服务。【参考方案2】:

运行您的代码会在以下行显示异常:

var RoleManager = serviceProvider.GetRequiredService<RoleManager<IdentityRole>>();

根据异常情况,您似乎缺少 startup.cs 文件中用于启用身份的配置。我看不到你的startup.cs,但我怀疑你错过了整个services.AddIdentity(...) 电话。

我建议您花一些时间阅读如何在 ASP.NET Core 中配置身份。 Microsoft Docs 总是一个很好的起点。

此外,您在问题中提到的second question 有很好的步骤。您特别需要查看第 2 步和第 3 步。

【讨论】:

我没有services.AddIdentity(...),但是,Visual Studio 生成了这个代码services.AddDefaultIdentity&lt;IdentityUser&gt;().AddDefaultUI(UIFramework.Bootstrap4).AddEntityFrameworkStores&lt;ApplicationDbContext&gt;();,这不是Identity 的配置吗?我用services.AddIdentity&lt;IdentityUser, IdentityRole&gt;().AddEntityFrameworkStores&lt;ApplicationDbContext&gt;().AddDefaultTokenProviders(); 替换了它,但我得到了'System.ArgumentNullException' in System.Private.CoreLib.dll'System.AggregateException' in System.Private.CoreLib.dll。我会再仔细阅读第二个问题。

以上是关于如何在 ASP.NET Core 2.2 中实现标识的主要内容,如果未能解决你的问题,请参考以下文章

如何在 ASP.NET Core 中实现自定义模型验证?

如何在 ASP.NET Core 2.2 中使用 IValidateOptions 验证配置设置?

如何在启用 ASP.NET Core 2.2 EndpointRouting 的情况下使用 RouteDataRequestCultureProvider?

如何在 ASP.net Core 中实现 dataTables 服务器端分页/搜索/排序

如何在asp.net core razor pages 2.2中设置日期格式和文化

如何在 asp net core 2.2 中间件中多次读取请求正文?