IdentityServer4源码解析_1_项目结构
程序员文章站
2022-04-14 16:11:18
目录 "identityserver4源码解析_1_项目结构" "identityserver4源码解析_2_元数据接口" "identityserver4源码解析_3_认证接口" "identityserver4源码解析_4_令牌发放接口" "identityserver4源码解析_5_查询用户信 ......
目录
- identityserver4源码解析_1_项目结构
- identityserver4源码解析_2_元数据接口
- identityserver4源码解析_3_认证接口
- identityserver4源码解析_4_令牌发放接口
- identityserver4源码解析_5_查询用户信息接口
- identityserver4源码解析_6_结束会话接口
- identityserver4源码解析_7_查询令牌信息接口
- identityserver4源码解析_8_撤销令牌接口
简介
security源码解析系列介绍了微软提供的各种认证架构,其中oauth2.0,openidconnect属于远程认证架构,所谓远程认证,是指token的颁发是由其他站点完成的。
identityserver4是基于openidconnect协议的认证中心框架,可以帮助我们快速搭建微服务认证中心。
初学者可能看到生涩的概念比较头疼,可以将oauth, openidconnect协议简单理解成需求文档,idsv4基于需求提供了一系列的api实现。
对于idsv还不太了解的可以看下面的资料,本系列主要学习梳理idsv4的源码,结合协议加深理解。
晓晨姐姐系列文章
官方文档
项目结构
项目地址如下
克隆到本地,项目结构如图
核心项目是identityserver4,其余的都是与微软框架集成、以及处理持久化的项目。
项目结构如图。endpoints文件夹就是接口文件,我们先看下依赖注入、中间件的代码,然后看下每个接口。
依赖注入
public static iidentityserverbuilder addidentityserver(this iservicecollection services) { var builder = services.addidentityserverbuilder(); builder .addrequiredplatformservices() .addcookieauthentication() .addcoreservices() .adddefaultendpoints() .addpluggableservices() .addvalidators() .addresponsegenerators() .adddefaultsecretparsers() .adddefaultsecretvalidators(); // provide default in-memory implementation, not suitable for most production scenarios builder.addinmemorypersistedgrants(); return builder; }
- addrequiredplatformservices - 注入平台服务
- ihttpcontextaccessor:httpcontext访问器
- identityserveroptions:配置类
public static iidentityserverbuilder addrequiredplatformservices(this iidentityserverbuilder builder) { builder.services.tryaddsingleton<ihttpcontextaccessor, httpcontextaccessor>(); builder.services.addoptions(); builder.services.addsingleton( resolver => resolver.getrequiredservice<ioptions<identityserveroptions>>().value); builder.services.addhttpclient(); return builder; }
- addcookieauthentication - 注入cookie服务
- 注入名称为idsrv的cookie认证架构
- 注入iauthenticationservice的实现identityserverauthenticationservice
- 注入iauthenticationhandlerprovider的实现federatedsignoutauthenticationhandlerprovider
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>(); return builder; }
- addcoreservices - 注入核心服务
/// <summary> /// adds the core services. /// </summary> /// <param name="builder">the builder.</param> /// <returns></returns> public static iidentityserverbuilder addcoreservices(this iidentityserverbuilder builder) { builder.services.addtransient<secretparser>(); builder.services.addtransient<secretvalidator>(); builder.services.addtransient<scopevalidator>(); builder.services.addtransient<extensiongrantvalidator>(); builder.services.addtransient<bearertokenusagevalidator>(); builder.services.addtransient<jwtrequestvalidator>(); // todo: remove in 3.0 #pragma warning disable cs0618 // type or member is obsolete builder.services.addtransient<backchannelhttpclient>(); #pragma warning restore cs0618 // type or member is obsolete builder.services.addtransient<returnurlparser>(); builder.services.addtransient<identityservertools>(); builder.services.addtransient<ireturnurlparser, oidcreturnurlparser>(); builder.services.addscoped<iusersession, defaultusersession>(); builder.services.addtransient(typeof(messagecookie<>)); builder.services.addcors(); builder.services.addtransientdecorator<icorspolicyprovider, corspolicyprovider>(); return builder; }
- adddefaultendpoints - 注入接口
- authorizecallbackendpoint:认证回调接口
- authorizeendpoint:认证接口
- checksessionendpoint:检查会话接口
- deviceauthorizationendpoint:设备认证接口
- discoveryendpoint:元数据键接口
- discoveryendpoint:元数据接口
- endsessioncallbackendpoint:结束会话回调接口
- endsessionendpoint:结束会话接口
- introspectionendpoint:查询令牌信息接口
- tokenrevocationendpoint:撤销令牌接口
- tokenendpoint:发放令牌接口
- userinfoendpoint:查询用户信息接口
注入所有默认接口,包括接口名称和地址。请求进来之后,路由类endpointrouter通过路由来寻找匹配的处理器。
public static iidentityserverbuilder adddefaultendpoints(this iidentityserverbuilder builder) { builder.services.addtransient<iendpointrouter, endpointrouter>(); builder.addendpoint<authorizecallbackendpoint>(endpointnames.authorize, protocolroutepaths.authorizecallback.ensureleadingslash()); builder.addendpoint<authorizeendpoint>(endpointnames.authorize, protocolroutepaths.authorize.ensureleadingslash()); builder.addendpoint<checksessionendpoint>(endpointnames.checksession, protocolroutepaths.checksession.ensureleadingslash()); builder.addendpoint<deviceauthorizationendpoint>(endpointnames.deviceauthorization, protocolroutepaths.deviceauthorization.ensureleadingslash()); builder.addendpoint<discoverykeyendpoint>(endpointnames.discovery, protocolroutepaths.discoverywebkeys.ensureleadingslash()); builder.addendpoint<discoveryendpoint>(endpointnames.discovery, protocolroutepaths.discoveryconfiguration.ensureleadingslash()); builder.addendpoint<endsessioncallbackendpoint>(endpointnames.endsession, protocolroutepaths.endsessioncallback.ensureleadingslash()); builder.addendpoint<endsessionendpoint>(endpointnames.endsession, protocolroutepaths.endsession.ensureleadingslash()); builder.addendpoint<introspectionendpoint>(endpointnames.introspection, protocolroutepaths.introspection.ensureleadingslash()); builder.addendpoint<tokenrevocationendpoint>(endpointnames.revocation, protocolroutepaths.revocation.ensureleadingslash()); builder.addendpoint<tokenendpoint>(endpointnames.token, protocolroutepaths.token.ensureleadingslash()); builder.addendpoint<userinfoendpoint>(endpointnames.userinfo, protocolroutepaths.userinfo.ensureleadingslash()); return builder; }
- addpluggableservices - 注入可插拔服务
public static iidentityserverbuilder addpluggableservices(this iidentityserverbuilder builder) { builder.services.tryaddtransient<ipersistedgrantservice, defaultpersistedgrantservice>(); builder.services.tryaddtransient<ikeymaterialservice, defaultkeymaterialservice>(); builder.services.tryaddtransient<itokenservice, defaulttokenservice>(); builder.services.tryaddtransient<itokencreationservice, defaulttokencreationservice>(); builder.services.tryaddtransient<iclaimsservice, defaultclaimsservice>(); builder.services.tryaddtransient<irefreshtokenservice, defaultrefreshtokenservice>(); builder.services.tryaddtransient<ideviceflowcodeservice, defaultdeviceflowcodeservice>(); builder.services.tryaddtransient<iconsentservice, defaultconsentservice>(); builder.services.tryaddtransient<icorspolicyservice, defaultcorspolicyservice>(); builder.services.tryaddtransient<iprofileservice, defaultprofileservice>(); builder.services.tryaddtransient<iconsentmessagestore, consentmessagestore>(); builder.services.tryaddtransient<imessagestore<logoutmessage>, protecteddatamessagestore<logoutmessage>>(); builder.services.tryaddtransient<imessagestore<endsession>, protecteddatamessagestore<endsession>>(); builder.services.tryaddtransient<imessagestore<errormessage>, protecteddatamessagestore<errormessage>>(); builder.services.tryaddtransient<iidentityserverinteractionservice, defaultidentityserverinteractionservice>(); builder.services.tryaddtransient<ideviceflowinteractionservice, defaultdeviceflowinteractionservice>(); builder.services.tryaddtransient<iauthorizationcodestore, defaultauthorizationcodestore>(); builder.services.tryaddtransient<irefreshtokenstore, defaultrefreshtokenstore>(); builder.services.tryaddtransient<ireferencetokenstore, defaultreferencetokenstore>(); builder.services.tryaddtransient<iuserconsentstore, defaultuserconsentstore>(); builder.services.tryaddtransient<ihandlegenerationservice, defaulthandlegenerationservice>(); builder.services.tryaddtransient<ipersistentgrantserializer, persistentgrantserializer>(); builder.services.tryaddtransient<ieventservice, defaulteventservice>(); builder.services.tryaddtransient<ieventsink, defaulteventsink>(); builder.services.tryaddtransient<iusercodeservice, defaultusercodeservice>(); builder.services.tryaddtransient<iusercodegenerator, numericusercodegenerator>(); builder.services.tryaddtransient<ibackchannellogoutservice, defaultbackchannellogoutservice>(); builder.addjwtrequesturihttpclient(); builder.addbackchannellogouthttpclient(); //builder.services.addhttpclient<backchannellogouthttpclient>(); //builder.services.addhttpclient<jwtrequesturihttpclient>(); builder.services.addtransient<iclientsecretvalidator, clientsecretvalidator>(); builder.services.addtransient<iapisecretvalidator, apisecretvalidator>(); builder.services.tryaddtransient<ideviceflowthrottlingservice, distributeddeviceflowthrottlingservice>(); builder.services.adddistributedmemorycache(); return builder; }
- addvalidators - 注入校验类
public static iidentityserverbuilder addvalidators(this iidentityserverbuilder builder) { // core builder.services.tryaddtransient<iendsessionrequestvalidator, endsessionrequestvalidator>(); builder.services.tryaddtransient<itokenrevocationrequestvalidator, tokenrevocationrequestvalidator>(); builder.services.tryaddtransient<iauthorizerequestvalidator, authorizerequestvalidator>(); builder.services.tryaddtransient<itokenrequestvalidator, tokenrequestvalidator>(); builder.services.tryaddtransient<iredirecturivalidator, strictredirecturivalidator>(); builder.services.tryaddtransient<itokenvalidator, tokenvalidator>(); builder.services.tryaddtransient<iintrospectionrequestvalidator, introspectionrequestvalidator>(); builder.services.tryaddtransient<iresourceownerpasswordvalidator, notsupportedresourceownerpasswordvalidator>(); builder.services.tryaddtransient<icustomtokenrequestvalidator, defaultcustomtokenrequestvalidator>(); builder.services.tryaddtransient<iuserinforequestvalidator, userinforequestvalidator>(); builder.services.tryaddtransient<iclientconfigurationvalidator, defaultclientconfigurationvalidator>(); builder.services.tryaddtransient<ideviceauthorizationrequestvalidator, deviceauthorizationrequestvalidator>(); builder.services.tryaddtransient<idevicecodevalidator, devicecodevalidator>(); // optional builder.services.tryaddtransient<icustomtokenvalidator, defaultcustomtokenvalidator>(); builder.services.tryaddtransient<icustomauthorizerequestvalidator, defaultcustomauthorizerequestvalidator>(); return builder; }
- addresponsegenerators - 注入响应生成类
public static iidentityserverbuilder addresponsegenerators(this iidentityserverbuilder builder) { builder.services.tryaddtransient<itokenresponsegenerator, tokenresponsegenerator>(); builder.services.tryaddtransient<iuserinforesponsegenerator, userinforesponsegenerator>(); builder.services.tryaddtransient<iintrospectionresponsegenerator, introspectionresponsegenerator>(); builder.services.tryaddtransient<iauthorizeinteractionresponsegenerator, authorizeinteractionresponsegenerator>(); builder.services.tryaddtransient<iauthorizeresponsegenerator, authorizeresponsegenerator>(); builder.services.tryaddtransient<idiscoveryresponsegenerator, discoveryresponsegenerator>(); builder.services.tryaddtransient<itokenrevocationresponsegenerator, tokenrevocationresponsegenerator>(); builder.services.tryaddtransient<ideviceauthorizationresponsegenerator, deviceauthorizationresponsegenerator>(); return builder; }
- adddefaultsecretparsers & adddefaultsecretvalidators
/// <summary> /// adds the default secret parsers. /// </summary> /// <param name="builder">the builder.</param> /// <returns></returns> public static iidentityserverbuilder adddefaultsecretparsers(this iidentityserverbuilder builder) { builder.services.addtransient<isecretparser, basicauthenticationsecretparser>(); builder.services.addtransient<isecretparser, postbodysecretparser>(); return builder; } /// <summary> /// adds the default secret validators. /// </summary> /// <param name="builder">the builder.</param> /// <returns></returns> public static iidentityserverbuilder adddefaultsecretvalidators(this iidentityserverbuilder builder) { builder.services.addtransient<isecretvalidator, hashedsharedsecretvalidator>(); return builder; }
identityserveroptions - 配置类
/// <summary> /// the identityserveroptions class is the top level container for all configuration settings of identityserver. /// </summary> public class identityserveroptions { /// <summary> /// gets or sets the unique name of this server instance, e.g. https://myissuer.com. /// if not set, the issuer name is inferred from the request /// </summary> /// <value> /// unique name of this server instance, e.g. https://myissuer.com /// </value> public string issueruri { get; set; } /// <summary> /// gets or sets the origin of this server instance, e.g. https://myorigin.com. /// if not set, the origin name is inferred from the request /// note: do not set a url or include a path. /// </summary> /// <value> /// origin of this server instance, e.g. https://myorigin.com /// </value> public string publicorigin { get; set; } /// <summary> /// gets or sets the value for the jwt typ header for access tokens. /// </summary> /// <value> /// the jwt typ value. /// </value> public string accesstokenjwttype { get; set; } = "at+jwt"; /// <summary> /// emits an aud claim with the format issuer/resources. that's needed for some older access token validation plumbing. defaults to false. /// </summary> public bool emitlegacyresourceaudienceclaim { get; set; } = false; /// <summary> /// gets or sets the endpoint configuration. /// </summary> /// <value> /// the endpoints configuration. /// </value> public endpointsoptions endpoints { get; set; } = new endpointsoptions(); /// <summary> /// gets or sets the discovery endpoint configuration. /// </summary> /// <value> /// the discovery endpoint configuration. /// </value> public discoveryoptions discovery { get; set; } = new discoveryoptions(); /// <summary> /// gets or sets the authentication options. /// </summary> /// <value> /// the authentication options. /// </value> public authenticationoptions authentication { get; set; } = new authenticationoptions(); /// <summary> /// gets or sets the events options. /// </summary> /// <value> /// the events options. /// </value> public eventsoptions events { get; set; } = new eventsoptions(); /// <summary> /// gets or sets the max input length restrictions. /// </summary> /// <value> /// the length restrictions. /// </value> public inputlengthrestrictions inputlengthrestrictions { get; set; } = new inputlengthrestrictions(); /// <summary> /// gets or sets the options for the user interaction. /// </summary> /// <value> /// the user interaction options. /// </value> public userinteractionoptions userinteraction { get; set; } = new userinteractionoptions(); /// <summary> /// gets or sets the caching options. /// </summary> /// <value> /// the caching options. /// </value> public cachingoptions caching { get; set; } = new cachingoptions(); /// <summary> /// gets or sets the cors options. /// </summary> /// <value> /// the cors options. /// </value> public corsoptions cors { get; set; } = new corsoptions(); /// <summary> /// gets or sets the content security policy options. /// </summary> public cspoptions csp { get; set; } = new cspoptions(); /// <summary> /// gets or sets the validation options. /// </summary> public validationoptions validation { get; set; } = new validationoptions(); /// <summary> /// gets or sets the device flow options. /// </summary> public deviceflowoptions deviceflow { get; set; } = new deviceflowoptions(); /// <summary> /// gets or sets the mutual tls options. /// </summary> public mutualtlsoptions mutualtls { get; set; } = new mutualtlsoptions(); }
useridentityserver - 中间件逻辑
- 执行校验
- baseurlmiddleware中间件:设置baseurl
- 配置cors跨域:corspolicyprovider根据client信息生成动态策略
- identityservermiddlewareoptions默认调用了useauthentication,所以如果使用identityserver不用重复注册authentication中间件
- 使用mutualtlstokenendpointmiddleware中间件:要求客户端、服务端都使用https,默认不开启
- 使用identityservermiddleware中间件:iendpointrouter根据请求寻找匹配的iendpointhandler,如果找到的话则由endpointhandler处理请求。
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(); options.authenticationmiddleware(app); app.usemiddleware<mutualtlstokenendpointmiddleware>(); app.usemiddleware<identityservermiddleware>(); return app; }
核心中间件identityservermiddleware的代码,逻辑比较清晰
- iendpointrouter路由类旬斋匹配接口
- 匹配接口处理请求返回结果iendpointresult
- iendpointresult执行结果,写入上下文,返回报文
public async task invoke(httpcontext context, iendpointrouter router, iusersession session, ieventservice events) { // this will check the authentication session and from it emit the check session // cookie needed from js-based signout clients. await session.ensuresessionidcookieasync(); try { var endpoint = router.find(context); if (endpoint != null) { _logger.loginformation("invoking identityserver endpoint: {endpointtype} for {url}", endpoint.gettype().fullname, context.request.path.tostring()); var result = await endpoint.processasync(context); if (result != null) { _logger.logtrace("invoking result: {type}", result.gettype().fullname); await result.executeasync(context); } return; } } catch (exception ex) { await events.raiseasync(new unhandledexceptionevent(ex)); _logger.logcritical(ex, "unhandled exception: {exception}", ex.message); throw; } await _next(context); }
看一下路由类的处理逻辑
之前adddefaultendpoints注入了所有默认接口,路由类可以通过依赖注入拿到所有接口信息,将请求地址与接口地址对比得到匹配的接口,然后从容器拿到对应的接口处理器。
public endpointrouter(ienumerable<endpoint> endpoints, identityserveroptions options, ilogger<endpointrouter> logger) { _endpoints = endpoints; _options = options; _logger = logger; } public iendpointhandler find(httpcontext context) { if (context == null) throw new argumentnullexception(nameof(context)); foreach(var endpoint in _endpoints) { var path = endpoint.path; if (context.request.path.equals(path, stringcomparison.ordinalignorecase)) { var endpointname = endpoint.name; _logger.logdebug("request path {path} matched to endpoint type {endpoint}", context.request.path, endpointname); return getendpointhandler(endpoint, context); } } _logger.logtrace("no endpoint entry found for request path: {path}", context.request.path); return null; } private iendpointhandler getendpointhandler(endpoint endpoint, httpcontext context) { if (_options.endpoints.isendpointenabled(endpoint)) { var handler = context.requestservices.getservice(endpoint.handler) as iendpointhandler; if (handler != null) { _logger.logdebug("endpoint enabled: {endpoint}, successfully created handler: {endpointhandler}", endpoint.name, endpoint.handler.fullname); return handler; } else { _logger.logdebug("endpoint enabled: {endpoint}, failed to create handler: {endpointhandler}", endpoint.name, endpoint.handler.fullname); } } else { _logger.logwarning("endpoint disabled: {endpoint}", endpoint.name); } return null; }
总结
主干流程大致如图
idsv的代码量还是比较大的,有很多的类,但是代码还是要写的挺规范清晰,梳理下来脉络还是很明了的。
推荐阅读
-
iOS开发中常见的项目文件与MVC结构优化思路解析
-
严蔚敏数据结构源码及习题解析
-
【源码解析】凭什么?spring boot 一个 jar 就能开发 web 项目
-
.NET Core实战项目之CMS 第三章 入门篇-源码解析配置文件及依赖注入
-
LibGdx学习过程(二)项目结构解析
-
【RocketMQ源码深度解析2】源码目录结构介绍&Remoting通信层
-
SpringBoot加载web项目流程源码解析
-
Hadoop源码学习笔记之NameNode启动流程分析一:源码环境搭建和项目模块及NameNode结构简单介绍
-
axios 源码解析(中) 代码结构
-
IdentityServer4源码解析_4_令牌发放接口