NetCore 3.1 将 IdentityServer4 与现有数据库和用户一起使用 table

NetCore 3.1 Use IdentityServer4 with existing database and Users table

我有一个名为 Users 的 table 现有数据库(它实际上是一个旧的 DotNetNuke 8.1(大约 2016 年)数据库 table 结构,包含 Users、UserRoles 等 tables ). 它不遵循当前 Microsoft 标识的结构,例如(AspNetUsers、AspNetUserRoles...等)。

我想在使用该数据库的NetCore 3.1 项目中添加一个身份验证层。 我设法将数据库 tables 构建到模型中并添加了数据库上下文 classes,这样我就可以访问用户 table.

我如何添加 IdentityServer4 以使用来自用户 table.This 的用户名及其密码进行身份验证:

public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
              
            services.AddDbContext<ApplicationDbContext>(option => option.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

            // added for identity server
            services.AddIdentityServer()
                .AddInMemoryApiResources(Configurations.Configuration.GetApis())
                .AddInMemoryClients(Configurations.Configuration.GetClients())
                .AddDeveloperSigningCredential();

            services.AddTransient<IResourceOwnerPasswordValidator, Configurations.ResourceOwnerPasswordValidator>();
            services.AddTransient<IProfileService, Configurations.ProfileService>();

            services.AddControllersWithViews();

          
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ApplicationDbContext dbContext)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseHttpsRedirection();

            app.UseRouting();

            // added for identity server
            app.UseIdentityServer();

            //added
            app.UseAuthentication();

            app.UseAuthorization();

            //added
            dbContext.Database.EnsureCreated();

            app.UseEndpoints(endpoints =>
            {

                endpoints.MapControllerRoute(
                   name: "default",
                   pattern: "{controller=Test}/{action=Index}/{id?}");
            });
        }
    }

IdentityServer4 的配置 class:

public static class Configuration
    {
        public static IEnumerable<ApiResource> GetApis() =>
            new List<ApiResource> 
            { 
                new ApiResource("ApiOne")  // registering one api with this name
            };

        public static IEnumerable<Client> GetClients() =>    // define a client that will consume above api
            new List<Client>
            {
                new Client
                {
                    ClientId = "resClient",
                    ClientSecrets = { new Secret("topsecret".ToSha256()) },   // make client secret more complex for production, can be made to expire


                    AllowedGrantTypes = GrantTypes.ClientCredentials,    // how client will request the access token

                    //define what the access token will be allowed to be used for, the scopes
                    AllowedScopes = { "ApiOne" }     // this client will be allowed to access Api One
                }
            };


    }

配置文件服务class:

public class ProfileService : IProfileService
    {
        public Task GetProfileDataAsync(ProfileDataRequestContext context)
        {
            context.IssuedClaims = context.Subject.Claims.ToList();
            return Task.FromResult(0);
        }

        public Task IsActiveAsync(IsActiveContext context)
        {
            return Task.FromResult(0);
        }
    }

我尝试使用用户名并从数据库传递(未散列,仅用于测试)但 return 始终是:

"unauthorized_client"

您正在获取 unauthorized_client 客户端,因为 resClient 配置为仅接受 ClientCredentials 授权 您需要修改客户端以接受 Password 授权(或者如果此客户端不需要 ClientCredentials,则只需密码):

                new Client
            {
                ClientId = "resClient",
                ClientSecrets = { new Secret("topsecret".ToSha256()) },   // make client secret more complex for production, can be made to expire


                AllowedGrantTypes = GrantTypes.ResourceOwnerPasswordAndClientCredentials,    // how client will request the access token

                //define what the access token will be allowed to be used for, the scopes
                AllowedScopes = { "ApiOne" }     // this client will be allowed to access Api One
            }

要解决您的 invalid_scope 错误:

将对 .AddInMemoryApiResources(Configurations.Configuration.GetApis()) 的调用替换为 .AddInMemoryApiScopes(Configurations.Configuration.GetApis()) 并将 GetApis() 更改为:

        public static IEnumerable<ApiScope> GetApis() =>
        new List<ApiScope> 
        { 
            new ApiScope("ApiOne")  // registering one api with this name
        };

这是让 IdentityServer4 在 ASP.NET Core 2.2 及更高版本中工作所需的配置文件示例。创建一个静态 class 并像您所做的那样在 UR 启动 class 中连接它

public static class InMemoryConfiguration
     {
          public static IEnumerable<ApiResource> ApiResources()
          {
               return new[] {
                    new ApiResource("yourapp", "Your App", new List<string>() {"role"})
                    {
                         ApiSecrets = { new Secret("topsecret".Sha256()) },
                         UserClaims = { JwtClaimTypes.Email, JwtClaimTypes.Role, JwtClaimTypes.Name, JwtClaimTypes.FamilyName, JwtClaimTypes.GivenName }
                    }
               };
          }

      public static IEnumerable<IdentityResource> GetIdentityResources()
      {
           return new List<IdentityResource>
           {
               new IdentityResources.OpenId(),
               new IdentityResources.Profile(),
               new IdentityResources.Email(),
               new IdentityResource("roles", "Your App Roles", new List<string>() {"role"})
           };
      }

      public static IEnumerable<Client> Clients()
      {
           return new[] {
                new Client
                {
                     ClientId = "resClient",
                     ClientSecrets = new [] { new Secret("topsecret".Sha256()) },
                     AllowedGrantTypes = GrantTypes.ResourceOwnerPasswordAndClientCredentials,
                     AllowedScopes = new [] { "yourapp" },
                     AllowedCorsOrigins = new [] {"http://localhost:8841", "http://localhost:8842"}
                },
                new Client
                {
                     ClientId = "mykabapp_native",
                     ClientSecrets = new [] { new Secret("mykabsecret".Sha256()) },
                     AllowedGrantTypes = GrantTypes.ResourceOwnerPasswordAndClientCredentials,
                     AllowedScopes = {
                          StandardScopes.OpenId,
                          StandardScopes.Profile,
                          StandardScopes.Email,
                          "mykabapp",
                          "roles"
                      },
                     AllowedCorsOrigins = new [] {"http://localhost:8841", "http://localhost:8842"}
                }
        };
      }

      public static IEnumerable<TestUser> Users()
      {
           return new[] {
                new TestUser
                {
                    SubjectId = "012345",
                    Username = "mail@userdomain.com",
                    Password = "password"
                },
                new TestUser
                {
                    SubjectId = "123456",
                    Username = "admin@userdomain.com",
                    Password = "password"
                }
           };
      }
 }
 

// AllowedGrantTypes = GrantTypes.ResourceOwnerPasswordAndClientCredentials,

注意上面一行。它真的很重要,即使您的客户端 ID 在客户端是正确的。您可以获得“unauthorized_client”错误