IdentityServer网页登陆-登陆原理
前言
现代程序开发中身份验证、授权是一件非常非常复杂的事情(各种登陆方式、各种授权需求、各种跳转跳、各种加解密,搞得得头皮发麻),因为事情本身复杂,所以没把这件事理清楚之前,无论你用什么语言、什么框架、什么方式都很难做到既简单又具有可扩展性。我的想法是既然我自己做不到,那就搞懂身份验证和授权是咋回事,再学习一套优秀的开源框架。这两件事其实就是一回事。
之前从asp.net core的源码开始学起,后来陆续看了asp.net core的身份验证和授权相关源码,之前也写过几篇相关博客。最近几天一直在看identityserver源码,目前对整套东西有个大概了解了,这个过程很痛苦也很有收获。按个人经验来说推荐如下学习方式:
- 先过一遍蒋老师的《asp.net core 3框架解密》,里面有asp.net core的原理,包括身份验证和授权的详解
- 过一遍IdentityServer官方文档
- 看IdentityServer源码
学完你会掌握asp.net core和identiyServer的机制,同时学学大牛的软件设计思路。
IdentityServer本身支持多种身份验证和授权的流程,这里只是针对普通mvc程序作为客户端,集成ids网页登陆的流程进行说明。搞懂这一个流程,其它流程也差不太远。这里只是将主体流程,里面涉及到具体步骤已经连接到在上一篇《》中。建议先看看官方文档的《Protecting an API using Client Credentials》、《Interactive Applications with ASP.NET Core》
写文章时IdentityServer4是4.x版本,下面IdentityServer简称ids/ids4,这里主要是根据源码讲原理,如果你只是想简单使用ids,看官方文档更合适,可以快速上手。
一、跳转到登录页idsServer/Account/Login(Get请求)
总结:根据请求参数决定显示哪些第三方登陆
当用户访问客户端(这里的mvc应用,对于ids来说就是客户端)受保护的页面时,若没有登陆则会跳转到ids的登陆页面。至于如何实现跳转的这是客户端配置的ids身份验证方案来完成的,不是本篇重点。
具体来说是get请求跳转到AccountController.Login(IIdentityServerInteractionService验证returnUrl中的参数,并根据这些参数创建一个表示当前请求的上下文对象AuthorizationRequest 此步骤很常用
这些步骤都是来设置viewModel的相关属性,这个viewModel决定视图页面要显示哪些内容
二、Post提交账号密码到idsServer/Account/Login
总结:验证用户账号密码 --> 加密得到的用户 --> 写入idsServer域名下的cookie --> 将用户重定向到"idsServer/connect/authenzation/callback"
用户输入完账号密码,连同前一步骤的returnUrl(里面包含授权请求的重要参数)Post提交到ids的AccountController.Login这个Action中,下面看看其核心步骤:
- 使用交互服务接口IIdentityServerInteractionService验证returnUrl中的参数,并根据这些参数创建一个表示当前请求的上下文对象AuthorizationRequest 此步骤很常用
- 否则若用户点击是取消,
- 则调用交互服务接口IIdentityServerInteractionService的DenyAuthorizationAsync在加密cookie中记录用户的拒绝授权的操作
- 将用户重定向到"idsServer/connect/authenzation/callback",授权的参数也继续传递过去
- 验证用户名和密码,若成功则从配置中获取用户实体,同时处罚UserLoginSuccessEvent事件
- 使用ids的本地身份验证方案(cookie)做登陆,写入加密的用户信息和相关的身份验证属性AuthenticationProperties
- 将用户重定向到"idsServer/connect/authenzation/callback",授权的参数也继续传递过去
三、ids使用AuthorizeCallbackEndpoint处理"callback回调
处理用户对授权scope的确认、ids中client、resouce和scope的配置最终得出可以授权哪些scope,最后生成code并跳转到mvc客户端回调地址
核心步骤:
- 通过本地身份验证方案(cookie)中解密得到用户信息
- 通过用户授权确认信息存取器IConsentMessageStore获取用户授权确认信息ConsentResponse,比如用户最终允许了哪些scope。由于之前这个信息存储在加密cookie中的,这时会删除掉
- 通过授权请求验证器IAuthorizeRequestValidator验证授权请求参数,然后转换为表示当前授权请求的AuthorizeRequestValidationResult
- 使用授权交互响应构建器IAuthorizeInteractionResponseGenerator检查用户是否需要登陆、授权确认信息、将最终授权的resource scope等信息设置到表示当前授权请求的对象中
- 通过授权响应构建器IAuthorizeResponseGenerator生成响应,由于这里是ids端的回调,因此此步骤主要是生成存储code的响应对象AuthorizeResponse
- 最后响应时动态创建一个form表单提交到mvc客户端的“mvc1/oidc-callback”地址
四、客户端携带client_id、client_securet、code等信息向ids请求accessToken/idToken
此步骤是ids提供的客户端库来完成的,暂时忽略。我们只要知道此时会携带重要的参数:client_id、client_securet、code 去向ids的Authenrize/token端点请求accessToken/idToken就行了
五、ids验证客户端密钥并发放token
客户端携带client_id|、密码、code和其它参数请求ids的Token端点,ids验证客户端密钥、code等,最后发放accessToken/idToken,核心任务如下:
- 请求由ids的TokenEndpoint接管,执行ProcessAsync
- 客户端密钥验证器IClientSecretValidator验证客户端的密钥
- token请求验证器ITokenRequestValidator验证当前请求,主要是对授权类型等参数进行验证,然后验证code
- 使用token响应构建器ITokenResponseGenerator创建响应TokenResponse
- 将TokenResponse包装为TokenResult,它类似mvc里的actionResult的设计,这个result对象将在ids的中间件中被执行,执行时响应token给mvc客户端
六、mvc客户端做本地登陆并跳转到redirectoryUrl
此步骤是ids提供的客户端库来完成的,暂时忽略。我们只要知道
客户端可以直接拿到用户标识openid,做本地登陆。如使用asp.net core常用的基于cookie的身份验证登陆,本质是加密用户信息和验证相关的属性存储到cookie里
也可以根据标识openid去mvc本地系统的用户管理模块中找到匹配的用户,若没有则自动或提示用户注册或绑定现有账号。最后以mvc本地的系统用户做登陆,此时登陆同理使用asp.net core基于cookie的登陆
七、结束
这里聊了下ids作为统一的身份验证和授权服务里中的网页登陆,类似于平时集成qq登陆的场景,里面主要描述了这个流程中的主要过程,涉及到具体步骤或类基本都有连接,可以点进去详细看。没有介绍基础知识,建议看源码学习ids时可以作为参考。这种场景我们平时可能不太会用,下一篇也许会写个常见的”资源所有者密码模式“的过程。