前言

现代程序开发中身份验证、授权是一件非常非常复杂的事情(各种登陆方式、各种授权需求、各种跳转跳、各种加解密,搞得得头皮发麻),因为事情本身复杂,所以没把这件事理清楚之前,无论你用什么语言、什么框架、什么方式都很难做到既简单又具有可扩展性。我的想法是既然我自己做不到,那就搞懂身份验证和授权是咋回事,再学习一套优秀的开源框架。这两件事其实就是一回事。

之前从asp.net core的源码开始学起,后来陆续看了asp.net core的身份验证和授权相关源码,之前也写过几篇相关博客。最近几天一直在看identityserver源码,目前对整套东西有个大概了解了,这个过程很痛苦也很有收获。按个人经验来说推荐如下学习方式:

  1. 先过一遍蒋老师的《asp.net core 3框架解密》,里面有asp.net core的原理,包括身份验证和授权的详解
  2. 过一遍IdentityServer官方文档
  3. 看IdentityServer源码

学完你会掌握asp.net core和identiyServer的机制,同时学学大牛的软件设计思路。

IdentityServer本身支持多种身份验证和授权的流程,这里只是针对普通mvc程序作为客户端,集成ids网页登陆的流程进行说明。搞懂这一个流程,其它流程也差不太远。这里只是将主体流程,里面涉及到具体步骤已经连接到在上一篇《IdentityServer4种的核心类》中。建议先看看官方文档的《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(returnUrl),returnUrl参数是客户端配置的ids身份验证方案生成的,里面包含ids授权需要的重要参数。(注意授权请求的参数并不是直接放在queryString里的,而是放在queryString里的returnUrl这个参数中的)returnUrl的格式如下:

/connect/authorize/callback?
client_id=mvc2&                      --客户端id好理解
redirect_uri=https://localhost:5003/signin-oidc&  --当ids生成code之后回调客户端的这个地址,客户端在这个里会携带客户端密钥和其它参数再次请求ids,以获取accessToken/idToken  
response_type=code&                    --希望ids响应的类型,
scope=openid profile&                   --客户端希望请求的资源范围
code_challenge=MstxPB26Nhnm2HvxVwz7TeFjyvQ6EDzvXp0xH-L7OYk& --目前不晓得啥用,应该是个签名密钥
code_challenge_method=S256&                    --目前不晓得啥用,应该是签名算法
response_mode=form_post&                      --当ids生成code之后以何种方式回调客户端的“/signin-oidc”,显然这里会动态生成一个form,然后post提交
nonce=637338663012806688.YWI3MjIxZGItMzlmOC00NmRiLTkz     --随机字符串,参与签名计算
MTMtNGFjNDJiMDczNWQzM2Q5NjE4ZjMtZmIwNS00ODAxLWI0ZmQtZ
TAxZDY2NDQyNDlk&                  
state=CfDJ8FuK5UM4huhIoCCo0DRW8LkJjSHkSLmZjj5kYC5BhPOFqfUWAq2Fq6QzWDrqM9W6j2oNMIRmg6Len1JTzHa5uhBh-5Ij0jzrg7vUd5Z-jE1KwRno    --客户端的一个状态字段,将来回调客户端时会原样携带回去
siu3W7hRLc2TKUeviyVd8zoBy1kVj5pmyhNWdahfQLU9lZgPQZBRe7U_itnUUo2S_hO4D1Rm7vq5uJwWTyn_00HNpZCMzVfZyWP2hCPGCRBCJtowq6yhbQ4qD_
L_jPo67MKEjLPEDolw857KkCKPYzu_HlSK1WyiKBTr1-H0yp19p0atpHNOKqtXyLbdY5Lsej0OcQLO99bE1v0LcvRJwyH758ZsbfLRaTUr7O6rI-wXBe6dp3ny
wkFcrW16la09ZPi8rvoUkDQZhhs8qA&

x-client-SKU=ID_NETSTANDARD2_0&
x-client-ver=5.5.0.0

下面是get请求Loigin方法的源码

1 public async Task<IActionResult> Login(string returnUrl)
2 {
3 var vm = await BuildLoginViewModelAsync(returnUrl);
4 if (vm.IsExternalLoginOnly)
5 return RedirectToAction("Challenge", "External", new { scheme = vm.ExternalLoginScheme, returnUrl });
6 return View(vm);
7 }

BuildLoginViewModelAsync用来创建一个包含第三发登陆信息的viewModel,用来显示到登陆页面上,如何确定要显示哪些第三方登陆的呢?,由于方法内容稍多,我下面直接说此方法的核心步骤:

  1. 使用交互服务接口IIdentityServerInteractionService验证returnUrl中的参数,并根据这些参数创建一个表示当前请求的上下文对象AuthorizationRequest 此步骤很常用
  2. 若前端有通过acr_values参数来指定希望通过哪种身份验证方案,若指定的方案是ids本地用户登陆,则不显示三方登陆,否则第三方登陆里只包含客户端指定的这个第三方登陆
  3. 若客户端没有指定,则获取ids允许的所有身份验证方案(启动配置时定义的支持的多种身份验证方案)
  4. 通过client_id参数从配置中找到客户端实体,看是否启用ids本地用户登陆。
  5. 客户端若配置了IdentityProviderRestrictions属性,则与步骤3中做交集来最终确认在页面要显示哪些第三登陆
  6. 若请求参数指定了LoginHint参数,则将其作为登陆的默认用户名

这些步骤都是来设置viewModel的相关属性,这个viewModel决定视图页面要显示哪些内容

二、Post提交账号密码到idsServer/Account/Login

总结:验证用户账号密码 --> 加密得到的用户 --> 写入idsServer域名下的cookie --> 将用户重定向到"idsServer/connect/authenzation/callback"

用户输入完账号密码,连同前一步骤的returnUrl(里面包含授权请求的重要参数)Post提交到ids的AccountController.Login这个Action中,下面看看其核心步骤:

  1. 使用交互服务接口IIdentityServerInteractionService验证returnUrl中的参数,并根据这些参数创建一个表示当前请求的上下文对象AuthorizationRequest 此步骤很常用
  2. 否则若用户点击是取消,
    1. 则调用交互服务接口IIdentityServerInteractionServiceDenyAuthorizationAsync在加密cookie中记录用户的拒绝授权的操作
    2. 将用户重定向到"idsServer/connect/authenzation/callback",授权的参数也继续传递过去
  3. 验证用户名和密码,若成功则从配置中获取用户实体,同时处罚UserLoginSuccessEvent事件
  4. 使用ids的本地身份验证方案(cookie)做登陆,写入加密的用户信息和相关的身份验证属性AuthenticationProperties
  5. 将用户重定向到"idsServer/connect/authenzation/callback",授权的参数也继续传递过去

三、ids使用AuthorizeCallbackEndpoint处理"callback回调

处理用户对授权scope的确认、ids中client、resouce和scope的配置最终得出可以授权哪些scope,最后生成code并跳转到mvc客户端回调地址

核心步骤:

  1. 通过本地身份验证方案(cookie)中解密得到用户信息
  2. 通过用户授权确认信息存取器IConsentMessageStore获取用户授权确认信息ConsentResponse,比如用户最终允许了哪些scope。由于之前这个信息存储在加密cookie中的,这时会删除掉
  3. 通过授权请求验证器IAuthorizeRequestValidator验证授权请求参数,然后转换为表示当前授权请求的AuthorizeRequestValidationResult
  4. 使用授权交互响应构建器IAuthorizeInteractionResponseGenerator检查用户是否需要登陆、授权确认信息、将最终授权的resource scope等信息设置到表示当前授权请求的对象中
  5. 通过授权响应构建器IAuthorizeResponseGenerator生成响应,由于这里是ids端的回调,因此此步骤主要是生成存储code的响应对象AuthorizeResponse
  6. 最后响应时动态创建一个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,核心任务如下:

  1. 请求由ids的TokenEndpoint接管,执行ProcessAsync
  2. 客户端密钥验证器IClientSecretValidator验证客户端的密钥
  3. token请求验证器ITokenRequestValidator验证当前请求,主要是对授权类型等参数进行验证,然后验证code
  4. 使用token响应构建器ITokenResponseGenerator创建响应TokenResponse
  5. 将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时可以作为参考。这种场景我们平时可能不太会用,下一篇也许会写个常见的”资源所有者密码模式“的过程。

IdentityServer网页登陆-登陆原理的更多相关文章

  1. Cookie及App登陆的原理

    1.Cookie Cookie意为"甜饼",是由W3C组织提出的.目前Cookie已经成为标准.由于HTTP是一种无状态的协议,服务器单从网络连接上无从知道客户身份.怎么办呢?就给 ...

  2. 我了解到的新知识之—Apple Captive Portal 网页认证登陆公共Wifi

    因为今天一个用户遇到选择公司WiFi后,无法弹出网页认证登陆界面的问题,随即上网搜索相关信息,因为公司内没有VPN,无法FQ,只能用bing来搜索一下相关信息了. Captive Portal听起来好 ...

  3. 微信公众平台网页授权登陆access_token误区

    公众平台里显示 每日获取access_token上线2000次,此access_token并非网页授权登陆的access_token大家不要混淆 1,网页授权登陆的access_token是没有上线的 ...

  4. PHP网页的工作原理

    网络基本概念 IP地址 唯一标识网络上的主机或设备. IP地址是由四段8位二进制构成,中间用小数点隔开.如:192.168.18.70 每一段取值0-255的十进制. 特殊的IP地址:127.0.0. ...

  5. IdentityServer4网页(单点)登陆入门

    前言 本篇说说ids中的网页登陆以及单点登陆的大致原理,主要是以基本跑通为目的,下一篇开始会详细说明集成ids网页登陆原理. 最好先熟悉以下知识: asp.net core asp.net core的 ...

  6. 无线热点登陆认证原理探究---captive portal

    什么是Captive Portal 大家肯定都连过公共场所的wifi热点,比如麦当劳等地方的.他们的wifi往往一连上去就会弹出一个要求登录或者微信关注之类的页面,只有在这个页面完成操作了才能正常访问 ...

  7. 无线热点登陆认证原理探究---captive portal 什么是Captive Portal

    什么是Captive Portal 大家肯定都连过公共场所的wifi热点,比如麦当劳等地方的.他们的wifi往往一连上去就会弹出一个要求登录或者微信关注之类的页面,只有在这个页面完成操作了才能正常访问 ...

  8. BurpSuite 爆破网页后台登陆

    由于 Burp Suite是由Java语言编写而成,所以你需要首先安装JAVA的运行环境,而Java自身的跨平台性,使得软件几乎可以在任何平台上使用.Burp Suite不像其他的自动化测试工具,它需 ...

  9. 结合Excel批量操作网页,模拟登陆

    有这样一个场景,客户的一批账户密码保存在Excel中,需要逐一登录,进行某些操作 从头开始来的话很麻烦,读取Excel,安装Web控件,主要是控件操作没有很方便,有没有类似原始js调用.jqurey调 ...

  10. oauth三方登陆的原理

    一 说明 OAuth是由Blaine Cook.Chris Messina.Larry Halff 及David Recordon共同发起的,目的在于为API访问授权提供一个开放的标准(resful和 ...

随机推荐

  1. PHP、JS、css、python、mysql 基本常用函数特殊方法记录

    html <a draggable="false">禁止拖拽</a> css .nowrap{word-break:keep-all;white-space ...

  2. [网鼎杯 2020 朱雀组]phpweb

    打开靶机,抓包分析,获得连个关键参数func和p,根据初始页面提示了解连个参数大概是功能和功能参数 测试func=system&p=ls提示hacker..说明有检测过滤 那么我们先读取源码看 ...

  3. Flink 状态编程

    概念 在Flink架构体系中,有状态计算可以说是Flink非常重要的特性之一 Flink优势: 支持高吞吐.低延迟.高性能 支持事件时间Event_time概念 支持有状态计算 有状态计算是指: 在程 ...

  4. 一文讲透 FPGA CDC 多bit跨时钟域同步-hand-shanking机制

    一.背景 数据的跨时钟域处理是FPGA开发过程中的常见问题,存在两种情况 慢时钟向快时钟同步:只需在快时钟域打两拍即可.其RTL如下: 打拍同步的原理:大家在初学FPGA时,经常听过FPGA中对信号打 ...

  5. KindleVocab 教程,Kindle导出查词记录成文本文档,Kindle导出查询单词记录导入Anki

    程序功能 因本人使用Kindle Mate导出觉得复杂,特意写了个自用的导出程序(有linux版本和win两个版本). 所以 KindleVocab 只有一个作用:导出Kindle查询过的生词和生词所 ...

  6. ubuntu 安装使用 mytop

    apt搜索一下 $ sudo apt search mytop Sorting... Done Full Text Search... Done mytop/focal,focal,now 1.9.1 ...

  7. PHP之常见问题

    汇总在PHP开发中遇到的一些问题 1.post提交参数缺失 场景: 在前端页面发起一个post提交的时候,查看payload中的数据是正常的, 但是在接收的时候,发现只有部分数据,算了一下,包含的数据 ...

  8. ubuntu 下的 nslookup 命令利用 127.0.0.53 查询主机名失败,而使用网关则正常的问题

    遇到一个奇怪的问题,ubuntu 下使用 KRDC 远程访问局域网主机时,连接主机名失败,使用 ip 则正常.通过 nslookup 命令发现,局域网主机名没有被正确解析(使用的是默认的 127.0. ...

  9. Sortable.js笔记

    1.前言 SortableJS是功能强大的JavaScript 拖拽库,更多配置项:Sortable.js中文网|配置 引入插件 <script src="https://cdn.bo ...

  10. WxPython跨平台开发框架之用户选择和标签组件的设计

    在系统的权限管理中,往往都会涉及到用户的选择处理,特别是基于角色的访问控制中,很多情况下需要用到选择用户的处理.本篇随笔,基于WxPython跨平台开发框架,采用原有开发框架成熟的一套权限系统理念,对 ...