上一篇文章我们介绍了Asp.net core中身份验证的相关内容,并通过下图描述了身份验证及授权的流程:
注:改流程图进行过修改,第三方用户名密码登陆后并不是直接获得code/id_token/access_token,而是登录后可以访问identityServer中受保护的资源(Authorize Endpoint),通过发起身份验证请求来实现授权码流程、隐式流程及混合流程来完成token的获取,它与直接通过用户名密码来获取token的Oauth2.0 Password GrantType方式是不一样的。
在asp.net core应用程序中,通过授权码流程可以使用第三方(IdentityServer)的用户名密码,经过一系列的token、userinfo获取,最后生成身份信息载体(Cookie),asp.net core应用程序使用cookie就能完成身份验证工作。这个过程对于用户来说,它与一般的asp.net core应用程序(特指基于asp.net core identity的应用程序)是没有任何区别的,都是通过用户名密码登录,然后就可以进入系统。对于应用程序来说它仍然是基于cookie来完成身份验证,只不过生成cookie所需的数据是第三方提供的而已。
但是单页应用由于其特殊性,其UI渲染工作及业务逻辑的处理都是由浏览器完成,服务器不具备相关功能(仅需静态文件传输即可),其次单页应用会存在跨域问题,所以cookie就不适合作为单页应用的身份信息载体,本文就介绍如何使用jwt来完成单页应用的身份验证。
主要内容有:
- 创建一个简单的单页应用项目
- 使用单页应用完成受保护资源访问
- oidc Client简介
- oidc-client.js的各种用法
- 小结
创建一个简单的单页应用项目
1、新建一个asp.net core的web应用程序:
2、添加oidc的js组件:
可以通过npm进行安装或者在Github上直接下载,以下为npm安装方法:
npm i oidc-client
完成之后在相关目录下可找到oidc-client相关文件:
将其复制到适合的位置即可。
添加Client时需要注意几个关键信息:
- 授权类型支持授权码类型;
- 不需要客户端密码;
- 允许跨域,允许客户端跨域访问IdentityServer;
参考如下,内存实例:
数据库数据:
4、添加基于oidc-client的登录、API访问以及登出业务逻辑代码App.js:
UserManager对象初始化:
使用UserManager实例进行登录跳转:
使用UserManager实例获取用户信息,然后通过用户信息中的access token访问受保护资源:
使用UserManager实例进行登出跳转:
5、添加功能页面Index.html,包含登录、API访问及登出功能:
6、添加用于处理授权码的重定向页面:
到此单页应用程序已经创建完毕,后面就使用该程序要介绍它是如何完成身份验证,并访问受保护资源的。
使用单页应用完成受保护资源访问
我们使用一个简单的asp.net core web api项目(本系列文章用过的)来进行演示,它对于普通API项目来说要点在于:
1、添加基于JwtBearer的身份验证处理器:
2、添加跨域处理,添加跨域策略配置:
3、在asp.net core应用请求管道中应用跨域配置:
4、受保护内容通过authorize特性进行标记:
一切准备就绪后运行三个应用程序,单页应用运行并打开index.html页面效果如下,一共有三个功能,登录、调用API以及登出:
登录:它实际上是调用oidc Client的signinRedirect方法,语义上来说它是通过重定向的方式进行登录,而它实际执行的效果如下:
跳转到了IdentityServer的登录页面,然后我们再看看它本质上是做了什么?
为了能够看清楚整个请求过程,本例在callback.html页面加入了调试断点:
断点位于signinRedirectCallback方法之后,也就是完成回调处理之后(这个时候已经完成token等信息的获取),跳转到index.html页面之前。
以下是输入用户名密码提交后命中断点时的相关请求信息:
由上图可以看到,当输入用户名密码提交后(第一个请求),由于通过了身份验证,那么继续完成授权码流程(第二个请求),授权码流程完成后携带授权码重定向到Client配置的重定向地址(第三个请求).
第三个请求就到了我们的callback.html页面,页面的加载首先请求了oidc-client.js文件,然后由UserManager的实例化以及signinRedirectCallback方法,来完成了后续请求,后续请求包含openid的配置信息请求、获取Token请求、获取用户信息(userinfo)请求以及检查会话请求。
以上一系列的请求结果就是在浏览器的会话存储中,我们可以找到相关的数据信息:
断点通过后就来到了index.html页面,并打印出登录用户信息:
点击Call API按钮后,程序将从存储信息中获取到access_token,携带access_token完成请求:
点击登出按钮后,程序将删除用户信息并跳转到IdentityServer的登出页面:
注:需要配置identityserver4的登出url:
oidc-client.js简介
前面的内容是基于oidc-client.js,即JavaScript版本的oidc客户端类库来实现的单页应用的,那么oidc-client.js到底为我们提供了什么功能呢?
oidc-client.js是一个支持OIDC和Oauth2.0协议的JavaScript类库,除此之外它还提供用户会话和Token的管理功能。类库中的核心类型是UserManager,它提供了用户登录、登出、用户信息管理等高层次的API,上面的例子中就是使用UserManager来完成的登录、用户信息(Access Token)获取以及登出的。
oidc-client.js或者直接说UserManager使用上需要注意以下几个方面:
但这里要注意的是由于以上代码对用户是可见的,所以Client的密码就省略了。
- 方法:提供了用户管理、登录、登出、以及相关回调方法,除此之外还有会话状态查询和开启/关闭静默刷新(token)的方法。登录/登出分为三种类型:跳转、静默和弹出,具体如何使用后续介绍。
- 属性:可以返回UserManager的配置、事件以及元数据服务。
- 事件:UserManager包含了8个事件,如用户登录/登出、access token过期等:
oidc-client.js的各种用法
弹出式登录/登出
弹出式登录/登出就是字面的意思,通过弹出窗口打开IdentityServer的登录/登出页面完成相应功能。
下图为弹出式登录(仅需调用UserManager的signinPopup方法即可):
注:回调页面需要使用signinPopupCallback:
下图为弹出式登出:
静默登录与静默刷新
静默登录和静默刷新指的就是signinSilent和startSilentRenew两个方法,而且需要注意的是startSilentRenew的原理实际上是关注了accessTokenExpiring事件,当token即将过期时调用signinSilent进行静默登录。
静默登录方式又有两种其一是基于会话的,其二是基于刷新token,其中刷新token的优先级较高,换句话就是刷新token存在的时候,它就默认使用刷新token进行登录,刷新token比较好理解,但是会话是什么呢?它实际上就是通过IdentityServer的登录后所保持的状态,文章最开始的流程图中提到过,我们之所以可以通过授权码流程进行授权是因为登录之后有权访问IdentityServer受保护的授权终结点,从而可以获取授权码及相关Token,那这里的原理就是浏览器保存了登录状态,所以可以再次访问授权终结点来获取并刷新Token信息。
基于会话的静默登录,下图为点击静默登录按钮后发起的请求信息,也就是正常的请求到授权码之后获取token及用户信息的过程:
需要注意的是回调页面需要使用signinSilentCallback方法,同时不再需要页面跳转:
基于刷新token的静默登录,在尝试刷新token登录之前首先需要获得刷新token,oidc中刷新token的获取是需要client支持offline_access的scope,同时在发起获取token时携带该scope:
配置完成后重新登录获取token即可在存储中找到刷新token信息 :
然后再次进行静默登录(相应client需要支持refresh_token的授权方式):
静默登录发起的请求信息:
响应信息中包含了新的token:
会话监听
会话监听是默认开启的,在正常登录状态下,通过新的浏览器窗口从identityserver中登出(目的是清除identityserver存储在浏览器的会话信息):
信息清除后它会立即尝试发起新的身份验证请求,但是返回信息中包含“需要登录”错误信息,可以在接收到相关错误信息时清除相关Token及用户信息,已达到单页应用随着IdentityServer会话结束而登出的效果:
小结
本篇文章介绍了单页应用使用IdentityServer进行身份验证的过程及oidc-client.js JavaScript类库在应用中的使用,oidc-client.js为我们适配了oidc协议,同时还提供了丰富的功能和机制,使用这个类库可以大大减少实际工作中的开发量。
说到单页应用的身份验证,它最根本的机制无非就是获得Access Token和Refresh Token,使用Access Token作为身份信息载体来完成身份验证,使用Refresh Token作为更新Access Token的钥匙,通过保证Access Token不过期来保证用户能够正常访问相关资源。
但如果使用Oauth2.0协议来实现单页应用的登录(Password授权模式)会存在一些问题,首先是单页应用对于授权服务器来说是不可信的,但是Password授权由单页应用发起,属于授权服务器的用户信息及密码都要经过不可信的单页应用,这会造成一些安全问题,其次使用Oauth2.0协议完成授权后,应用与授权服务器之间实际上就没有任何关联了,授权服务器不保留用户会话,也无法实现用户登出联动的功能(即一方登出可以通知另一方),这些也正是IdentityServer4或者说OIDC协议所处理的内容。
下篇文章,我们将继续以IdentityServer4或者说OIDC的会话管理开始,深入聊一聊它们的登录与登出。
参考:
- 从零搭建一个IdentityServer——聊聊Asp.net core中的身份验证与授权
OpenIDConnect是一个身份验证服务,而Oauth2.0是一个授权框架,在前面几篇文章里通过IdentityServer4实现了基于Oauth2.0的客户端证书(Client_Credenti ...
- 从零搭建一个IdentityServer——目录(更新中...)
从零搭建一个IdentityServer--项目搭建 从零搭建一个IdentityServer--集成Asp.net core Identity 从零搭建一个IdentityServer--初识Ope ...
- 从零搭建一个IdentityServer——会话管理与登出
在上一篇文章中我们介绍了单页应用是如何使用IdentityServer完成身份验证的,并且在讲到静默登录以及会话监听的时候都提到会话(Session)这一概念,会话指的是用户与系统之间交互过程,反过来 ...
- 从零搭建一个IdentityServer——资源与访问控制
IdentityServer作为授权服务器它的最终目的是用于对资源进行管控,这里所说的资源有两种,其一是API资源,实际上也就是OIDC协议中客户端(RP)所需要访问的一系列受保护的资源(API),授 ...
- 从零搭建一个IdentityServer——项目搭建
本篇文章是基于ASP.NET CORE 5.0以及IdentityServer4的IdentityServer搭建,为什么要从零搭建呢?IdentityServer4本身就有很多模板可以直接生成一个可 ...
- 从零搭建一个IdentityServer——初识OpenIDConnect
上一篇文章实现了IdentityServer4与Asp.net core Identity的集成,可以使用通过identity注册功能添加的用户,以Password的方式获取Access token, ...
- 从零搭建一个IdentityServer——集成Asp.net core Identity
前面的文章使用Asp.net core 5.0以及IdentityServer4搭建了一个基础的验证服务器,并实现了基于客户端证书的Oauth2.0授权流程,以及通过access token访问被保护 ...
- 从零搭建一个SpringCloud项目之Feign搭建
从零搭建一个SpringCloud项目之Feign搭建 工程简述 目的:实现trade服务通过feign调用user服务的功能.因为trade服务会用到user里的一些类和接口,所以抽出了其他服务需要 ...
- 单页应用 - Token 验证
单页应用 - Token 验证 转:https://juejin.im/post/58da720b570c350058ecd40f 第一次接触单页应用,记录公司项目关于Token验证知识. Token ...
随机推荐
- LVS-DR 模式
SNAT(Source Network Address Translation)源地址转换,类似家里路由器设置,内网地址向外访问时,发起访问的内网ip地址转换为指定的 IP 地址 DNAT(Desti ...
- 二进制安装kubernetes(三) kube-controller-manager组件安装
Controller Manager简介 详细介绍请参考链接:Kubernetes组件之kube-controller-manager Controller Manager作为集群内部的管理控制中心, ...
- 【原创】Linux虚拟化KVM-Qemu分析(九)之virtio设备
背景 Read the fucking source code! --By 鲁迅 A picture is worth a thousand words. --By 高尔基 说明: KVM版本:5.9 ...
- 网络安全知识--PHP代码审计/Web For Pantesters 的 XSS
用到 ** WEB FOR Pentester** 注意区分单引号双引号. 常见代码 审计工具 wamp,dwva,zvuldrill,burpsuite,seay源代码审计系统... 1 xss W ...
- LeetCode6 Z字形排列
题目描述是从上到下,从左到右Z字形排列. 找规律.这种形式一般都是mod x 余数有规律.然后写的时候围绕x构造,而非判断,代码会简单一些. 设行数为r 先观察r=5的情况 发现第0行的字符原始ind ...
- QXDM和QCAT软件使用指南
一.传送门 链接:https://pan.baidu.com/s/1i55YXnf 密码:v6nw 二.QXDM,QPST和QCAT的简单说明 QXDM,QPST和QCAT是Qualcomm高通公司针 ...
- Leetcode(10)-正则表达式匹配
给定一个字符串 (s) 和一个字符模式 (p).实现支持 '.' 和 '*' 的正则表达式匹配. '.' 匹配任意单个字符. '*' 匹配零个或多个前面的元素. 匹配应该覆盖整个字符串 (s) ,而不 ...
- HDU 6706 huntian oy(杜教筛 + 一些定理)题解
题意: 已知\(f(n,a,b)=\sum_{i=1}^n\sum_{j=1}^igcd(i^a-j^a,i^b-j^b)[gcd(i,j)=1]\mod 1e9+7\),\(n\leq1e9\),且 ...
- XCTF攻防世界web进阶练习—mfw
XCTF攻防世界web进阶练习-mfw题目为mfw,没有任何提示.直接打开题目,是一个网站 大概浏览一下其中的内容,看到其中url变化其实只是get的参数的变化查看它的源码,看到有一个?page=fl ...
- range()函数的使用、while循环、for-in循环等
一.range()函数 用于直接生成一个整数序列 创建range对象的三种方式: (1)range(stop) 创建一个(0,stop)之间的整数序列,步长为1 (2)range(start,s ...