close
文章出處

最近花了一點時間,閱讀了IdentityServer的源碼,大致了解項目整體的抽象思維、面向對象的重要性; 生產環境如果要使用 IdentityServer3 ,主要涉及授權服務,資源服務的部署負載的問題,客戶端(clients),作用域(scopes),票據(token)一定都要持久化, 客戶端與作用域的持久化只需要實現 IClientStore 與 IScopeStore 的接口,可以自己實現,也可以直接使用 IdentityServer3 自身的擴展 IdentityServer3.EntityFramework

image

Package

核心類庫
Install-Package IdentityServer3
IdentityServer 核心庫,只支持基于內存的客戶端信息與用戶信息配置

配置信息持久化
客戶端,作用域,票據的持久化 ,支持的擴展有兩個,一個基于 EF,另外一個使用MongoDb(社區支持)
Install-Package IdentityServer3.EntityFramework
Install-Package IdentityServer.v3.MongoDb

用戶持久化

用戶的持久化支持 MembershipReboot 與 ASP.NET Identity 兩種
Install-Package IdentityServer3.MembershipReboot
Install-Package IdentityServer3.AspNetIdentity

其他插件

WS-Federation
Install-Package IdentityServer3.WsFederation
Access token validation middleware(驗證中間件)
Install-Package IdentityServer3.AccessTokenValidation

配置信息持久化(Entity Framework support for Clients, Scopes, and Operational Data)

客戶端(clients)與作用域(scopes)的持久化

客戶端與作用域的持久化只需要實現 IClientStore 與 IScopeStore 的接口,默認EF 在 IdentityServerServiceFactory 實現了 RegisterClientStore 與 RegisterScopeStore 兩個擴展方法,也可以使用 RegisterConfigurationServices 方法,默認包含以上兩個擴展方法合集;RegisterOperationalServices 擴展方法實現 IAuthorizationCodeStore, ITokenHandleStore, IRefreshTokenStore, and IConsentStore 功能等。

可以在 IdentityServer3.EntityFramework 的項目中找到數據庫的初始SQL

ER 關系OAuth項目結構

image

IdentityServerServiceFactoryExtensions 類擴展 IdentityServerServiceFactory 實現方法來持久化信息,最后 Registration 到接口上

public static class IdentityServerServiceFactoryExtensions
    {
        public static void RegisterOperationalServices(this IdentityServerServiceFactory factory, EntityFrameworkServiceOptions options)
        {
            if (factory == null) throw new ArgumentNullException("factory");
            if (options == null) throw new ArgumentNullException("options");

            factory.Register(new Registration<IOperationalDbContext>(resolver => new OperationalDbContext(options.ConnectionString, options.Schema)));
            factory.AuthorizationCodeStore = new Registration<IAuthorizationCodeStore, AuthorizationCodeStore>();
            factory.TokenHandleStore = new Registration<ITokenHandleStore, TokenHandleStore>();
            factory.ConsentStore = new Registration<IConsentStore, ConsentStore>();
            factory.RefreshTokenStore = new Registration<IRefreshTokenStore, RefreshTokenStore>();
        }

        public static void RegisterConfigurationServices(this IdentityServerServiceFactory factory, EntityFrameworkServiceOptions options)
        {
            factory.RegisterClientStore(options);
            factory.RegisterScopeStore(options);
        }

        public static void RegisterClientStore(this IdentityServerServiceFactory factory, EntityFrameworkServiceOptions options)
        {
            if (factory == null) throw new ArgumentNullException("factory");
            if (options == null) throw new ArgumentNullException("options");

            factory.Register(new Registration<IClientConfigurationDbContext>(resolver => new ClientConfigurationDbContext(options.ConnectionString, options.Schema)));
            factory.ClientStore = new Registration<IClientStore, ClientStore>();
            factory.CorsPolicyService = new ClientConfigurationCorsPolicyRegistration(options);
        }
        
        public static void RegisterScopeStore(this IdentityServerServiceFactory factory, EntityFrameworkServiceOptions options)
        {
            if (factory == null) throw new ArgumentNullException("factory");
            if (options == null) throw new ArgumentNullException("options");

            factory.Register(new Registration<IScopeConfigurationDbContext>(resolver => new ScopeConfigurationDbContext(options.ConnectionString, options.Schema)));
            factory.ScopeStore = new Registration<IScopeStore, ScopeStore>();
        }
    }

TokenCleanup 類負責定時清除過期的票據信息

public class TokenCleanup
    {
        private readonly static ILog Logger = LogProvider.GetCurrentClassLogger();

        EntityFrameworkServiceOptions options;
        CancellationTokenSource source;
        TimeSpan interval;

        public TokenCleanup(EntityFrameworkServiceOptions options, int interval = 60)
        {
            if (options == null) throw new ArgumentNullException("options");
            if (interval < 1) throw new ArgumentException("interval must be more than 1 second");

            this.options = options;
            this.interval = TimeSpan.FromSeconds(interval);
        }

        public void Start()
        {
            if (source != null) throw new InvalidOperationException("Already started. Call Stop first.");
            
            source = new CancellationTokenSource();
            Task.Factory.StartNew(()=>Start(source.Token));
        }
        
        public void Stop()
        {
            if (source == null) throw new InvalidOperationException("Not started. Call Start first.");

            source.Cancel();
            source = null;
        }

        public async Task Start(CancellationToken cancellationToken)
        {
            while (true)
            {
                if (cancellationToken.IsCancellationRequested)
                {
                    Logger.Info("CancellationRequested");
                    break;
                }

                try
                {
                    await Task.Delay(interval, cancellationToken);
                }
                catch
                {
                    Logger.Info("Task.Delay exception. exiting.");
                    break;
                }

                if (cancellationToken.IsCancellationRequested)
                {
                    Logger.Info("CancellationRequested");
                    break;
                }

                await ClearTokens();
            }
        }

        public virtual IOperationalDbContext CreateOperationalDbContext()
        {
            return new OperationalDbContext(options.ConnectionString, options.Schema);
        }

        private async Task ClearTokens()
        {
            try
            {
                Logger.Info("Clearing tokens");
                using (var db = CreateOperationalDbContext())
                {
                    var query =
                        from token in db.Tokens
                        where token.Expiry < DateTimeOffset.UtcNow
                        select token;

                    db.Tokens.RemoveRange(query);

                    await db.SaveChangesAsync();
                }
            }
            catch(Exception ex)
            {
                Logger.ErrorException("Exception cleaning tokens", ex);
            }
        }
    }

配置Idsv授權服務

Startup 類修改

    /// <summary>
    /// OAuth2 服務配置
    /// </summary>
    public class Startup
    {
        /// <summary>
        /// 配置Idsv授權服務
        /// </summary>
        /// <param name="app"></param>
        public void Configuration(IAppBuilder app)
        {

            #region OAuth 2.0 服務端初始化
            /*
            //配置內存的數據持久化到DB
            Factory = new IdentityServerServiceFactory()
                          .UseInMemoryClients(Clients.Get())
                          .UseInMemoryScopes(Scopes.Get())
                          .UseInMemoryUsers(Users.Get()),
            */

            //EF配置
            var ef = new EntityFrameworkServiceOptions
            {
                ConnectionString = DbSetting.OAuth2,
            };
            var redis = ConnectionMultiplexer.Connect(CacheSetting.Redis);
            var factory = new IdentityServerServiceFactory();
            //注冊Client與Scope的實現
            factory.RegisterConfigurationServices(ef);
            //注冊Token實現
            factory.RegisterOperationalServices(ef);
            /*
                //注冊用戶(基于用戶表)
               factory.UseInMemoryUsers(new List<InMemoryUser>()
               {
                   //測試用戶
                   new InMemoryUser()
                   {
                       Username = "irving",
                       Password = "123456",
                       Subject = "1",
                       Claims = new[]
                       {
                           new Claim(IdentityServer3.Core.Constants.ClaimTypes.GivenName, "Bob"),
                           new Claim(IdentityServer3.Core.Constants.ClaimTypes.FamilyName, "Smith")
                       }

                   }
               });
               */

            //自定義用戶服務
            factory.UserService = new Registration<IUserService>(resolver => AutofacDependencyResolver.Current.RequestLifetimeScope.Resolve<IdSvrUserService>());
            //自定義頁面
            //factory.ViewService = new Registration<IViewService, IdSvrViewService>();
            factory.ViewService = new Registration<IViewService, IdSvrMvcViewService<LoginController>>();

            factory.Register(new Registration<HttpContext>(resolver => HttpContext.Current));
            factory.Register(new Registration<HttpContextBase>(resolver => new HttpContextWrapper(resolver.Resolve<HttpContext>())));
            factory.Register(new Registration<HttpRequestBase>(resolver => resolver.Resolve<HttpContextBase>().Request));
            factory.Register(new Registration<HttpResponseBase>(resolver => resolver.Resolve<HttpContextBase>().Response));
            factory.Register(new Registration<HttpServerUtilityBase>(resolver => resolver.Resolve<HttpContextBase>().Server));
            factory.Register(new Registration<HttpSessionStateBase>(resolver => resolver.Resolve<HttpContextBase>().Session));

            //Redis
            var clientStoreCache = new ClientStoreCache(redis);
            var scopeStoreCache = new ScopeStoreCache(redis);
            var userServiceCache = new UserServiceCache(redis);
            //注冊客戶端緩存-
            factory.ConfigureClientStoreCache(new Registration<ICache<Client>>(clientStoreCache));
            //注冊作用域緩存
            factory.ConfigureScopeStoreCache(new Registration<ICache<IEnumerable<Scope>>>(scopeStoreCache));
            //注冊用戶緩存
            factory.ConfigureUserServiceCache(new Registration<ICache<IEnumerable<Claim>>>(userServiceCache));

            //idsv配置
            app.UseIdentityServer(new IdentityServerOptions
            {
                SiteName = "Embedded OAuth2 Service",
                EnableWelcomePage = true,
                Factory = factory,
                RequireSsl = false,
                LoggingOptions = new LoggingOptions(),
                SigningCertificate = new X509Certificate2(string.Format(@"{0}\identityServer\idsrv3test.pfx", AppDomain.CurrentDomain.BaseDirectory), "idsrv3test"),
                EventsOptions = new EventsOptions
                {
                    RaiseSuccessEvents = true,
                    RaiseErrorEvents = true,
                    RaiseFailureEvents = true,
                    RaiseInformationEvents = true
                }
            });
            //factory.CorsPolicyService = new Registration<ICorsPolicyService>(new DefaultCorsPolicyService { AllowAll = true });

            //啟動清除過期票據定時器
            var cleanToken = new TokenCleanup(ef, 20);
            cleanToken.Start();

            #endregion

            #region OAuth 2.0 管理后臺 初始化
            //管理員功能 初始化
            app.Map("/admin", adminApp =>
            {
                var factoryAdmin = new IdentityAdmin.Configuration.IdentityAdminServiceFactory();
                //注入配置
                factoryAdmin.Configure();
                //注冊管理員
                adminApp.UseIdentityAdmin(new IdentityAdmin.Configuration.IdentityAdminOptions
                {
                    Factory = factoryAdmin,
                    //AdminSecurityConfiguration =
                });
            });
            #endregion 
        }
    }

 客戶端模式問題

  • 客戶端,作用域,票據的持久化 [OK]
  • 限制客戶端每天獲得新票據的次數
  • 票據過期刪除的策略 [OK]
  • 授權服務器客戶端信息緩存策略 [OK]
  • 資源服務器票據驗證的緩存策略 [OK]
  • 作用域權限范圍控制
  • ClientId 與 ClientSecret 的生成規則 [OK]
  • 密碼模式用戶的身份驗證 https://github.com/IdentityServer/IdentityServer3.AspNetIdentity

REFER:
Deployment
https://identityserver.github.io/Documentation/docsv2/advanced/deployment.html


不含病毒。www.avast.com
arrow
arrow
    全站熱搜
    創作者介紹
    創作者 AutoPoster 的頭像
    AutoPoster

    互聯網 - 大數據

    AutoPoster 發表在 痞客邦 留言(0) 人氣()