1.首先再github 上下載源代碼
(這里我下載的是:3.1.x):https://github.com/IdentityServer/IdentityServer4/blob/master/docs/index.rst
2.打開源代碼,
適當的編譯下,重新配置下nuget,這里就不展開了
3.打開示例項目:
IdentityServer4\samples\Quickstarts\3_AspNetCoreAndApis\src\IdentityServer
這里以這個示例為准,也可以是別的
4.准備:
把identityserver4的源代碼引用到示例里,把nuget上的包刪掉,引用IdentityServer4源代碼為依賴向,編譯:
備注:可能會出問題,反正我是碰到了一堆的問題,有的依賴不行,有的包不行,源代碼netcore的版本是3.1 把IdentityServer 改成3.1..(此處自己解決,有問題留言)
5.正式開始:
5.1 internal class IdentityServerAuthenticationService : IAuthenticationService
這個類繼承 IAuthenticationService ,包認證服務封裝了。
public IdentityServerAuthenticationService( Decorator<IAuthenticationService> decorator, IAuthenticationSchemeProvider schemes, ISystemClock clock, IUserSession session, IdentityServerOptions options, ILogger<IdentityServerAuthenticationService> logger) { _inner = decorator.Instance;//這里又把認證服務給傳了進來,很多默認的調用時用這個里面 _schemes = schemes; _clock = clock; _session = session; _options = options; _logger = logger; }
5.2 internal class FederatedSignoutAuthenticationHandlerProvider : IAuthenticationHandlerProvider
(為authenticationScheme和請求提供適當的IAuthenticationHandler實例)
IAuthenticationHandler 根據請求創建,以處理針對特定方案的身份驗證。
這里也有一個,在這里故技重施
5.3 他們是怎么個邏輯呢,看看這里
public static IIdentityServerBuilder AddCookieAuthentication(this IIdentityServerBuilder builder) { builder.Services.AddAuthentication(IdentityServerConstants.DefaultCookieAuthenticationScheme) .AddCookie(IdentityServerConstants.DefaultCookieAuthenticationScheme) .AddCookie(IdentityServerConstants.ExternalCookieAuthenticationScheme); builder.Services.AddSingleton<IConfigureOptions<CookieAuthenticationOptions>, ConfigureInternalCookieOptions>(); builder.Services.AddSingleton<IPostConfigureOptions<CookieAuthenticationOptions>, PostConfigureInternalCookieOptions>(); builder.Services.AddTransientDecorator<IAuthenticationService, IdentityServerAuthenticationService>(); builder.Services.AddTransientDecorator<IAuthenticationHandlerProvider, FederatedSignoutAuthenticationHandlerProvider>(); //這個函數,是identity自己封裝的,里面做了不少操作,主要是把系統的對象加了個殼Decorator,和上面的邏輯對應,然后用自己把系統的給頂了 return builder; }
internal static void AddDecorator<TService>(this IServiceCollection services) { var registration = services.LastOrDefault(x => x.ServiceType == typeof(TService)); if (registration == null) { throw new InvalidOperationException("Service type: " + typeof(TService).Name + " not registered."); } if (services.Any(x => x.ServiceType == typeof(Decorator<TService>))) { throw new InvalidOperationException("Decorator already registered for type: " + typeof(TService).Name + "."); } services.Remove(registration); if (registration.ImplementationInstance != null) { var type = registration.ImplementationInstance.GetType(); var innerType = typeof(Decorator<,>).MakeGenericType(typeof(TService), type); services.Add(new ServiceDescriptor(typeof(Decorator<TService>), innerType, ServiceLifetime.Transient)); services.Add(new ServiceDescriptor(type, registration.ImplementationInstance)); } else if (registration.ImplementationFactory != null) { services.Add(new ServiceDescriptor(typeof(Decorator<TService>), provider => { return new DisposableDecorator<TService>((TService)registration.ImplementationFactory(provider)); }, registration.Lifetime)); } else { var type = registration.ImplementationType; var innerType = typeof(Decorator<,>).MakeGenericType(typeof(TService), registration.ImplementationType); services.Add(new ServiceDescriptor(typeof(Decorator<TService>), innerType, ServiceLifetime.Transient)); services.Add(new ServiceDescriptor(type, type, registration.Lifetime)); } } }
5.4 internal class PostConfigureInternalCookieOptions : IPostConfigureOptions<CookieAuthenticationOptions>
這個是驗證用的參數,執行函數: GetHandlerAsync 獲取IAuthenticationHandler時創建的參數,
這里面有幾個比較有意思的地方:
public void PostConfigure(string name, CookieAuthenticationOptions options) { var scheme = _idsrv.Authentication.CookieAuthenticationScheme ?? _authOptions.Value.DefaultAuthenticateScheme ?? _authOptions.Value.DefaultScheme; if (name == scheme) { _idsrv.UserInteraction.LoginUrl = _idsrv.UserInteraction.LoginUrl ?? options.LoginPath;//這里LoginPath有一個默認的地址 _idsrv.UserInteraction.LoginReturnUrlParameter = _idsrv.UserInteraction.LoginReturnUrlParameter ?? options.ReturnUrlParameter;//還記着返回的url _idsrv.UserInteraction.LogoutUrl = _idsrv.UserInteraction.LogoutUrl ?? options.LogoutPath;//這里也有個默認的地址 _logger.LogDebug("Login Url: {url}", _idsrv.UserInteraction.LoginUrl); _logger.LogDebug("Login Return Url Parameter: {param}", _idsrv.UserInteraction.LoginReturnUrlParameter); _logger.LogDebug("Logout Url: {url}", _idsrv.UserInteraction.LogoutUrl); _logger.LogDebug("ConsentUrl Url: {url}", _idsrv.UserInteraction.ConsentUrl); _logger.LogDebug("Consent Return Url Parameter: {param}", _idsrv.UserInteraction.ConsentReturnUrlParameter); _logger.LogDebug("Error Url: {url}", _idsrv.UserInteraction.ErrorUrl); _logger.LogDebug("Error Id Parameter: {param}", _idsrv.UserInteraction.ErrorIdParameter); } }
看下cookie的參數:
5.5 來看看 UseIdentityServer 干了些啥
public static IApplicationBuilder UseIdentityServer(this IApplicationBuilder app, IdentityServerMiddlewareOptions options = null) { app.Validate(); app.UseMiddleware<BaseUrlMiddleware>(); app.ConfigureCors();//這里應該時處理跨域的 // it seems ok if we have UseAuthentication more than once in the pipeline -- // this will just re-run the various callback handlers and the default authN // handler, which just re-assigns the user on the context. claims transformation // will run twice, since that's not cached (whereas the authN handler result is) // related: https://github.com/aspnet/Security/issues/1399 if (options == null) options = new IdentityServerMiddlewareOptions();//這里竟然解決了一個bug,!!!!具體是啥我也沒看懂 options.AuthenticationMiddleware(app); app.UseMiddleware<MutualTlsTokenEndpointMiddleware>(); app.UseMiddleware<IdentityServerMiddleware>(); return app; }
可以看到這里有初始化執行的 ,也有3個中間件:BaseUrlMiddleware MutualTlsTokenEndpointMiddleware IdentityServerMiddleware
a. app.Validate(); 這個函數就是啟動時檢測的,里面一堆的控制台輸出。有個比較有意思的函數,下面:
internal static void Validate(this IApplicationBuilder app) { var loggerFactory = app.ApplicationServices.GetService(typeof(ILoggerFactory)) as ILoggerFactory; if (loggerFactory == null) throw new ArgumentNullException(nameof(loggerFactory)); var logger = loggerFactory.CreateLogger("IdentityServer4.Startup"); logger.LogInformation("Starting IdentityServer4 version {version}", typeof(IdentityServerApplicationBuilderExtensions).Assembly.GetName().Version.ToString()); var scopeFactory = app.ApplicationServices.GetService<IServiceScopeFactory>(); using (var scope = scopeFactory.CreateScope()) { var serviceProvider = scope.ServiceProvider; //下面這個函數,檢查對應的服務是否注冊,也是很有意思 TestService(serviceProvider, typeof(IPersistedGrantStore), logger, "No storage mechanism for grants specified. Use the 'AddInMemoryPersistedGrants' extension method to register a development version."); TestService(serviceProvider, typeof(IClientStore), logger, "No storage mechanism for clients specified. Use the 'AddInMemoryClients' extension method to register a development version."); TestService(serviceProvider, typeof(IResourceStore), logger, "No storage mechanism for resources specified. Use the 'AddInMemoryIdentityResources' or 'AddInMemoryApiResources' extension method to register a development version."); var persistedGrants = serviceProvider.GetService(typeof(IPersistedGrantStore)); if (persistedGrants.GetType().FullName == typeof(InMemoryPersistedGrantStore).FullName) { logger.LogInformation("You are using the in-memory version of the persisted grant store. This will store consent decisions, authorization codes, refresh and reference tokens in memory only. If you are using any of those features in production, you want to switch to a different store implementation."); } var options = serviceProvider.GetRequiredService<IdentityServerOptions>(); ValidateOptions(options, logger); ValidateAsync(serviceProvider, logger).GetAwaiter().GetResult(); } }
b.看看 BaseUrlMiddleware,下面時核心代碼:
public async Task Invoke(HttpContext context) { var request = context.Request; if (_options.PublicOrigin.IsPresent()) { context.SetIdentityServerOrigin(_options.PublicOrigin); } context.SetIdentityServerBasePath(request.PathBase.Value.RemoveTrailingSlash()); await _next(context); }