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”错误
我有一个名为 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”错误