IDENTITYSERVER 3入门工具包:安装IDENTITYSERVER 3,ASP.NET身份和实体框架
设置IdentityServer3的新实例时,需要为每个项目做一些事情。
在为IdentityServer3设置一个简单的启动套件(其中也包含用于用户的IdentityManager和用于客户端的admin)时,按照我的步骤,这将是一个小型教程/系列。
这是建立新项目时快速开始使用IdentityServer的起点。这也可用作入门和从头开始设置IdentityServer 3的指南,以及了解所有丢失部分的指南。
设置项目
我们将从创建一个空的ASP.NET Web应用程序开始,它将作为项目的宿主应用程序。
由于我们不需要该项目的任何默认模板,因此我们可以选择创建一个Empty项目。
接下来,是使用https设置开发环境。为此,我们需要打开项目的属性并将Web-> Project url设置为https:// url。
切记单击“创建虚拟目录”按钮以启用URL。
为了能够处理内置资产,我们需要在Web.config文件中启用设置。
<configuration>
...
<system.webServer>
<modules runAllManagedModulesForAllRequests="true" />
</system.webServer>
</configuration>
如果启动IdentityServer时遇到的欢迎页面没有任何有效的CSS,JS或图像,则您可能会错过“ RunAllManagedModulesForAllRequests”设置。
现在我们准备继续并开始安装软件包。
设置IDENTITYSERVER 3核心功能
安装基本软件包
完成项目设置后,我们将安装一些软件包,IdentityServer3安装IdentityServer的基础,IdentityServer3.AspNetIdentity以支持ASP.NET Identity,IdentityServer3.EntityFramework,以支持使用EntityFramework从SQL Server获得持久数据,Microsoft.AspNet.Identity.EntityFramework创建ASP.NET身份实体,Microsoft.Owin能够在启动时设置IdentityServer
Install-Package IdentityServer3
Install-Package IdentityServer3.AspNetIdentity
Install-Package IdentityServer3.EntityFramework
Install-Package Microsoft.Owin.Host.SystemWeb
Install-Package Microsoft.AspNet.Identity.EntityFramework
如果您希望服务器是自托管的,则可以创建一个控制台应用程序并安装包Microsoft.Owin.Host.HttpListner和Microsoft.Owin.Hosting而不是Microsoft.Owin.Host.SystemWeb
设置一些先决条件
常数
首先,我们将设置Constants.cs,该文件将用于在整个过程中包含一些常量。在这里,我们为将在入门工具包以及IdentityServer的核心端点中使用的连接字符串定义名称。
// <copyright file="Constants.cs">
// 2017 - Johan Boström
// </copyright>
namespace IdentityServer3.StarterKit
{
public static class Constants
{
public const string ConnectionStringName = "AspId";
public class Routes
{
public const string Core = "/ids";
public const string IdMgr = "/idm";
public const string IdAdm = "/ida";
}
}
}
证书
我们还需要有一个可以用来对令牌进行签名的证书。出于测试目的,我从IdentityServers github下载了测试证书,可以在这里找到。然后将证书添加到项目中,并将构建操作更改为“嵌入式资源”。
现在,我们需要能够读取证书,这可以通过创建类证书并以流形式读取文件,然后创建X509Certificate2来完成。
// <copyright file="Certificate.cs">
// 2017 - Johan Boström
// </copyright>
using System.IO;
using System.Security.Cryptography.X509Certificates;
namespace IdentityServer3.StarterKit.Config
{
public static class Certificate
{
public static X509Certificate2 Get()
{
var assembly = typeof(Certificate).Assembly;
using (var stream = assembly.GetManifestResourceStream("IdentityServer3.StarterKit.Config.idsrv3test.pfx"))
// Should be the path to the embeded certificate
{
return new X509Certificate2(ReadStream(stream), "idsrv3test");
}
}
private static byte[] ReadStream(Stream input)
{
var buffer = new byte[16 * 1024];
using (var ms = new MemoryStream())
{
int read;
while ((read = input.Read(buffer, 0, buffer.Length)) > 0)
ms.Write(buffer, 0, read);
return ms.ToArray();
}
}
}
}
数据库模型
现在,我们可以开始设置模型,以便在将数据存储到数据库时供用户使用,该模型可用于在条目上保留额外的信息。在用户上,我们将添加一个名字和一个姓氏以便以后输入。除此之外,由于该模型将继承IdentityUser的所有需求,因此现在不需要更多信息。
// <copyright file="User.cs">
// 2017 - Johan Boström
// </copyright>
using Microsoft.AspNet.Identity.EntityFramework;
namespace IdentityServer3.StarterKit.Models
{
public class User : IdentityUser
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
}
语境
接下来是设置数据库上下文,这是通过创建一个类并从IdentityDbContext继承并使用我们创建的用户模型以及一些直接来自Microsoft.AspNet.Identity.EntityFramework的模型来完成的。
// <copyright file="Context.cs">
// 2017 - Johan Boström
// </copyright>
using IdentityServer3.StarterKit.Models;
using Microsoft.AspNet.Identity.EntityFramework;
namespace IdentityServer3.StarterKit.Db
{
public class Context : IdentityDbContext<User, IdentityRole, string,IdentityUserLogin, IdentityUserRole, IdentityUserClaim>
{
public Context(string connString)
: base(connString)
{
}
}
}
专卖店
现在已经有了上下文,我们可以创建一个UserStore和RoleStore,系统可以使用它们来创建,查找,更新和删除用户和商店。我们将通过继承Microsoft.AspNet.Identity.EntityFramework中的商店来创建它们。
// <copyright file="UserStore.cs">
// 2017 - Johan Boström
// </copyright>
using IdentityServer3.StarterKit.Db;
using IdentityServer3.StarterKit.Models;
using Microsoft.AspNet.Identity.EntityFramework;
namespace IdentityServer3.StarterKit.Stores
{
public class UserStore : UserStore<User, IdentityRole, string, IdentityUserLogin, IdentityUserRole, IdentityUserClaim>
{
public UserStore(Context context)
: base(context)
{
}
}
}
// <copyright file="RoleStore.cs">
// 2017 - Johan Boström
// </copyright>
using IdentityServer3.StarterKit.Db;
using Microsoft.AspNet.Identity.EntityFramework;
namespace IdentityServer3.StarterKit.Stores
{
public class RoleStore : RoleStore<IdentityRole>
{
public RoleStore(Context context)
: base(context)
{
}
}
}
管理人员
// <copyright file="UserManager.cs">
// 2017 - Johan Boström
// </copyright>
using IdentityServer3.StarterKit.Factories;
using IdentityServer3.StarterKit.Models;
using IdentityServer3.StarterKit.Stores;
using Microsoft.AspNet.Identity;
namespace IdentityServer3.StarterKit.Managers
{
public class UserManager : UserManager<User, string>
{
public UserManager(UserStore store)
: base(store)
{
ClaimsIdentityFactory = new ClaimsFactory();
}
}
}
// <copyright file="RoleManager.cs">
// 2017 - Johan Boström
// </copyright>
using IdentityServer3.StarterKit.Stores;
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.EntityFramework;
namespace IdentityServer3.StarterKit.Managers
{
public class RoleManager : RoleManager<IdentityRole>
{
public RoleManager(RoleStore store)
: base(store)
{
}
}
}
创建CLAIMSIDENTITY
AspNet身份使用ClaimsIdentity作为身份类型,并且为了能够在需要设置ClaimsIdentityFactory之前,从创建的用户模型中创建此实例。这样,我们将能够提取添加到用户模型中的名字和姓氏,并将其添加为可能的声明。
// <copyright file="ClaimsIdentityFactory.cs">
// 2017 - Johan Boström
// </copyright>
using System.Security.Claims;
using System.Threading.Tasks;
using IdentityServer3.StarterKit.Models;
using Microsoft.AspNet.Identity;
namespace IdentityServer3.StarterKit.Factories
{
public class ClaimsIdentityFactory : ClaimsIdentityFactory<User, string>
{
public ClaimsIdentityFactory()
{
UserIdClaimType = Core.Constants.ClaimTypes.Subject;
UserNameClaimType = Core.Constants.ClaimTypes.PreferredUserName;
RoleClaimType = Core.Constants.ClaimTypes.Role;
}
public override async Task<ClaimsIdentity> CreateAsync(UserManager<User, string> manager, User user, string authenticationType)
{
var ci = await base.CreateAsync(manager, user, authenticationType);
if (!string.IsNullOrWhiteSpace(user.FirstName))
ci.AddClaim(new Claim("given_name", user.FirstName));
if (!string.IsNullOrWhiteSpace(user.LastName))
ci.AddClaim(new Claim("family_name", user.LastName));
return ci;
}
}
}
用户服务
现在我们只需要一个AspNetIdentityUserService实现,该实现是我们之前安装的IdentityServer3.AspNetIdentity包的一部分,因此它将使用新的用户模型。这样我们就可以开始将所有丢失的部分放在一起。
// <copyright file="UserService.cs">
// 2017 - Johan Boström
// </copyright>
using IdentityServer3.AspNetIdentity;
using IdentityServer3.StarterKit.Managers;
using IdentityServer3.StarterKit.Models;
namespace IdentityServer3.StarterKit.Services
{
public class UserService : AspNetIdentityUserService<User, string>
{
public UserService(UserManager userManager)
: base(userManager)
{
}
}
}
配置IDENTITYSERVER
设置默认范围,用户和客户端
首先,我们可以设置一些值,这些值可以用作我们可以操作和添加的数据的起点。仅当表中没有其他实体时,才会添加以下这些值。对于我决定将默认范围添加到数据库的范围,我添加了一个用户,以便我们有一个用户可以登录并尝试使用该系统。我还添加了一个客户端,可用于在入门套件系列的后续部分中进行构建。
// <copyright file="DefaultSetup.cs">
// 2017 - Johan Boström
// </copyright>
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using IdentityServer3.Core.Models;
using IdentityServer3.EntityFramework;
using IdentityServer3.StarterKit.Db;
using IdentityServer3.StarterKit.Managers;
using IdentityServer3.StarterKit.Models;
using IdentityServer3.StarterKit.Stores;
using Microsoft.AspNet.Identity;
namespace IdentityServer3.StarterKit.Config
{
public class DefaultSetup
{
public static void Configure(EntityFrameworkServiceOptions options)
{
using (var db = new ScopeConfigurationDbContext(options.ConnectionString, options.Schema))
{
if (!db.Scopes.Any())
{
foreach (var s in StandardScopes.All)
{
var e = s.ToEntity();
db.Scopes.Add(e);
}
foreach (var s in StandardScopes.AllAlwaysInclude)
{
var e = s.ToEntity();
db.Scopes.Add(e);
}
db.SaveChanges();
}
}
using (var db = new Context(options.ConnectionString))
{
if (!db.Users.Any())
{
using (var userManager = new UserManager(new UserStore(db)))
{
var defaultUserPassword = "skywalker"; // Must be atleast 6 characters
var user = new User
{
UserName = "administrator",
FirstName = "Luke",
LastName = "Skywalker",
Email = "luke.skywalker@email.com",
EmailConfirmed = true
};
userManager.Create(user, defaultUserPassword);
userManager.AddClaim(user.Id,
new Claim(Core.Constants.ClaimTypes.WebSite, "https://www.johanbostrom.se/"));
}
db.SaveChanges();
}
}
using (var db = new ClientConfigurationDbContext(options.ConnectionString, options.Schema))
{
if (!db.Clients.Any())
{
var defaultHybridClient = new Client
{
ClientName = "Default Hybrid Client",
ClientId = "default.hybrid",
Flow = Flows.Hybrid,
ClientSecrets = new List<Secret>
{
new Secret("default.hybrid.password".Sha256())
},
AllowedScopes = new List<string>
{
Core.Constants.StandardScopes.OpenId,
Core.Constants.StandardScopes.Profile,
Core.Constants.StandardScopes.Email,
Core.Constants.StandardScopes.Roles,
Core.Constants.StandardScopes.Address,
Core.Constants.StandardScopes.Phone,
Core.Constants.StandardScopes.OfflineAccess
},
ClientUri = "https://localhost:44300/",
RequireConsent = false,
AccessTokenType = AccessTokenType.Reference,
RedirectUris = new List<string>(),
PostLogoutRedirectUris = new List<string>
{
"https://localhost:44300/"
},
LogoutSessionRequired = true
};
db.Clients.Add(defaultHybridClient.ToEntity());
db.SaveChanges();
}
}
}
}
}
映射,注册和配置
现在剩下的就是将所有内容放在一起并映射IdentityServer核心。我决定为IAppBuilder创建一个带有证书的扩展。在这里,我们根据设置的常量配置EntityFramework(EF),创建IdentityServerServiceFactory并根据EF配置注册服务。我们还注册我们创建的UserService服务并运行默认配置设置。最后,它运行带有工厂和一些调试参数的UseIdentityServer扩展。
// <copyright file="AppBuilderExtensions.cs">
// 2017 - Johan Boström
// </copyright>
using System.Security.Cryptography.X509Certificates;
using IdentityServer3.Core.Configuration;
using IdentityServer3.Core.Services;
using IdentityServer3.EntityFramework;
using IdentityServer3.StarterKit.Config;
using IdentityServer3.StarterKit.Db;
using IdentityServer3.StarterKit.Managers;
using IdentityServer3.StarterKit.Services;
using IdentityServer3.StarterKit.Stores;
using Owin;
namespace IdentityServer3.StarterKit.Extensions
{
public static class AppBuilderExtensions
{
public static IAppBuilder MapCore(this IAppBuilder app, X509Certificate2 signingCertificate)
{
app.Map(Constants.Routes.Core, coreApp =>
{
var efConfig = new EntityFrameworkServiceOptions
{
ConnectionString = Constants.ConnectionStringName
};
var factory = new IdentityServerServiceFactory();
factory.RegisterConfigurationServices(efConfig);
factory.RegisterOperationalServices(efConfig);
factory.RegisterClientStore(efConfig);
factory.RegisterScopeStore(efConfig);
factory.Register(new Registration<UserManager>());
factory.Register(new Registration<UserStore>());
factory.Register(new Registration<Context>(resolver => new Context(Constants.ConnectionStringName)));
factory.UserService = new Registration<IUserService, UserService>();
DefaultSetup.Configure(efConfig);
coreApp.UseIdentityServer(new IdentityServerOptions
{
Factory = factory,
SigningCertificate = signingCertificate,
SiteName = "IdentityServer3 Starter Kit",
LoggingOptions = new LoggingOptions
{
EnableKatanaLogging = true
},
EventsOptions = new EventsOptions
{
RaiseFailureEvents = true,
RaiseInformationEvents = true,
RaiseSuccessEvents = true,
RaiseErrorEvents = true
}
});
});
return app;
}
}
}
最后一个难题是Owin创业公司。我创建了一个名为Startup.cs的文件,并为IAppBuilder运行了核心映射扩展。
// <copyright file="Startup.cs">
// 2017 - Johan Boström
// </copyright>
using IdentityServer3.StarterKit;
using IdentityServer3.StarterKit.Config;
using IdentityServer3.StarterKit.Extensions;
using Microsoft.Owin;
using Owin;
[assembly: OwinStartup(typeof(Startup))]
namespace IdentityServer3.StarterKit
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
var certificate = Certificate.Get();
app.MapCore(certificate);
}
}
}
做完了!
好吧,最后一切都应该做好。运行时,我们现在应该能够访问https:// localhost:44300 / ids并看到欢迎屏幕。我们还应该能够单击权限页面的链接,然后使用我们的管理员用户登录。
所有代码示例和完整的入门工具包项目都可以在我的GitHub页面上找到,并且由于这是系列文章的一部分,因此该代码将通过后续部分的更多功能不断进行更新。