变形精怪

树有年轮,人有皱纹

导航

IdentityServer4网页(单点)登陆入门

Posted on 2020-08-23 16:03  变形精怪  阅读(1601)  评论(0编辑  收藏  举报

前言

本篇说说ids中的网页登陆以及单点登陆的大致原理,主要是以基本跑通为目的,下一篇开始会详细说明集成ids网页登陆原理。 

最好先熟悉以下知识:

  • asp.net core
  • asp.net core的身份验证和基于策略的授权
  • identityServer官方文档过一遍

推荐蒋老师的《asp.net core 3 框架解密

场景

你在访问一个网站登陆时,可以选择输入账号密码登陆,也可以选择第三方登陆,如:QQ、微博账号等。登陆流程我就不废话了。假设是QQ登陆,我们这里可以通过ids4来实现QQ服务器来向第三方应用提供身份验证的功能

  1. 我们有多个MVC应用,假如是mvc1、mvc2,
  2. 希望统一由IndentityServer来做用户管理,假如这个服务叫idsServer
  3. 用户在登陆mvc1时自动跳转到idsServer的登陆页面,登陆成功后mvc1能拿到一个代表此用户的id(一个加密的字符串)
  4. 在我们后续请求mvc1时随时可以拿到用户的id
  5. 当请求mvc2时,由于是另一给应用,没登陆的情况下会跳转到idsServer去做登陆
  6. idsServer检测到这个浏览器之前在mvc1中做过登陆,直接返回用户id给mvc2

到此实现了mvc应用集成ids登陆,并实现了单点登陆。步骤5、6有点玄乎,下面会说明。ids4针对用户来说只是做身份验证(登陆),也就是识别出当前用户是谁,最终体现就是我们的应用可以拿到当前用户的id,ids4不负责用户的应用程序功能的授权,比如通常理解的基于角色的菜单、按钮权限

环境搭建

按官方文档的如下步骤可以搭建环境:

  1. https://identityserver4.readthedocs.io/en/latest/quickstarts/1_client_credentials.html
  2. https://identityserver4.readthedocs.io/en/latest/quickstarts/2_interactive_aspnetcore.html

我们这里使用更直接一点的方式,使用它提供的项目模板一步到位

1、安装ids4的项目模板

dotnet new -i IdentityServer4.Templates

2、根据模板创建ids4项目

dotnet new is4inmem

此模板创建会直接帮你创建要给立即可用的ids服务应用,里面的客户端、资源、用户都是以内存的形式定义的。直接F5就可用跑起来

3、在ids服务端中注册mvc1、mvc2的配置

就是在ids登记下这俩客户端,我是ids,你俩要让我来帮你们做登陆得先来我这里登个记对吧

在Config.Clients中添加如下配置:

new Client
{
//客户端id
    ClientId = "mvc1",
//客户端密钥
    ClientSecrets = { new Secret("secret".Sha256()) },
//授权模式为code
    AllowedGrantTypes = GrantTypes.Code,
//ids发放code时要回调客户端的地址
    RedirectUris = { "https://localhost:5002/signin-oidc" },
//完成在ids中注销后回调客户端的这个地址
    PostLogoutRedirectUris = { "https://localhost:5002/signout-callback-oidc" },
//ids允许此客户端访问这些scope
    AllowedScopes = new List<string>
    {
        IdentityServerConstants.StandardScopes.OpenId,
        IdentityServerConstants.StandardScopes.Profile
    }
}    

mvc2的配置一样。

4、新建mvc1、mvc2客户端

5、配置mvc1客户端,mvc2类似略了

5.1、调整俩项目启动监听的端口,防止3个项目的端口冲突,我这里ids用的5001,mvc1用的5002,mvc2用的5003

5.2、引用nuget包

install-package Microsoft.AspNetCore.Authentication.OpenIdConnect

5.3、在startup.cs中做配置

 1 public void ConfigureServices(IServiceCollection services)
 2 {
 3     services.AddControllersWithViews();
 4     JwtSecurityTokenHandler.DefaultMapInboundClaims = false;//还没研究过它是干啥的
 5     services.AddAuthentication(options => //注册asp.net core 身份验证核心服务,并配置
 6     {
 7         options.DefaultScheme = "Cookies";//默认的身份验证方案名
 8         options.DefaultChallengeScheme = "oidc";//用来跳转到dis登录页的身份验证方案名
 9         //注意这俩配置与下面注册的身份验证方案的名字对应
10     })
11     .AddCookie("Cookies")//注册asp.net core 默认的基于cookie的身份验证方案
12     .AddOpenIdConnect("oidc", options =>//注册ids为我们提供的oidc身份验证方案
13     {
14         options.Authority = "https://localhost:5001";//配置ids的根路径
15         options.ClientId = "mvc1";//此客户但的id
16         options.ClientSecret = "secret";//此客户端的密钥
17         options.ResponseType = "code";//授权模式
18         options.SaveTokens = true;//是否将最后获取的idToken和accessToken存储到默认身份验证方案中
19     });
20 }

5.4、在startup.cs中做配置启用asp.net core的身份验证中间件

1 public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
2 {
3     //略...
4     app.UseAuthentication();
5     app.UseAuthorization();
6     //略...
7 }

5.5、找个Controller的Action来充当受保护的页面,比如HomeController.Index

[Authorize]
public IActionResult Index()
{
        return View();
}

5.6、为了容易看到效果,可用修改下首页的视图,显示下当前登陆用户的信息

 1 @using Microsoft.AspNetCore.Authentication
 2 <h2>Claims</h2>
 3 <dl>
 4     @foreach (var claim in User.Claims)
 5     {
 6         <dt>@claim.Type</dt>
 7         <dd>@claim.Value</dd>
 8     }
 9 </dl>
10 <h2>Properties</h2>
11 <dl>
12     @foreach (var prop in (await Context.AuthenticateAsync()).Properties.Items)
13     {
14         <dt>@prop.Key</dt>
15         <dd>@prop.Value</dd>
16     }
17 </dl>

跑起来

在解决方案上右键->属性

 

 

 Ctrl + F5 走起...

此时由于mvc1和2的HomeController.Index是受保护的资源,首次访问因为用户并没有登陆,因此会调转到ids的登录页去

 

 

 录入测试账号和密码,ailice点击登陆,此时会跳转到mvc1的首页,因为已登陆成功,所以此时页面可用正常方案

此时若去访问mvc2的首页https://localhost:5003/你会发现它会跳转以下,最后直接就可用访问,并不需要我们再登陆了,这就是所谓的单点登陆。

如何注销?

注销时要清楚本地登陆和ids那边的登陆状态,在mvc1和2的HomeController中加入如下Action

public IActionResult Logout()
{
    return SignOut("Cookies", "oidc");
}

此时访问下这个Action就可用注销了。

主体流程

  1. 首先用户请求mvc1的受保护页面,mvc1的授权策略检测用户未登录,发出一个质询,由ids客户端库提供的身份验证处理器(在startup中配置的那个"oidc"的身份验证方案)处理这个质询,组织请求参数并跳重定向用户转到idsServer的登录页
  2. idsServer的AccountController.Login接收请求,通过相互服务接口IIdentityServerInteractionService对当前请求做验证(客户端啊、请求的scope啊、等等..),验证成功的话,得到一个结果AuthorizationRequest,表示当前授权请求,里面包含客户端id及其它参数
  3. 根据结果组织一个ViewModel,主要是决定是否显示第三方登陆(客户端请求时指定了希望哪种登陆方式?客户端配置时指定了支持哪些验证方式?idsServer默认支持哪些验证方式?)
  4. 假如用户使用账号密码登陆,输入后提交
  5. AccountController.Login Post接收请求,做步骤2一样的事,验证下客户端以及其它参数的验证,若通过则验证用户账号密码,若成功则得到用户实体(ids中注册的用户信息)
  6. ids自己做本地登陆,将用户信息加密存储到cookie中,然后跳转到自己的/connect/authenraztion/callback终结点
  7. 在/callback终结点中先验证客户端提交的各参数,生成临时code,回调客户端的".../signin-oidc"
  8. 客户端携带clientid 密钥 code 之类的参数找ids请求idToken和accessToken
  9. ids返回idToken和accessToken,mvc1服务端存储它们,然后将用户标识加密存储到用户的cookie中
  10. 用户后续携带用户标识cookie请求mvc1就可用了
  11. mvc1有时候需要携带accessToken访问被ids保护的第三方接口
  12. 当用户发起注销调用Home/logout时,在mvc1中注册的两个身份验证方案都会执行,"Cookies"将情况用户本地保存用户标识的cookie,“oidc”会与ids通信,删除ids存到用户浏览器中的cookie,此时ids还会以某种机智通知到其它已登陆的客户端,如何通知的后面再说

单点登陆的重点

按流程看,用户浏览器存储了两份代表用户标识的cookie,一个在idsServer域名下,要给在mvc1域名下,当mvc跳转到ids登录页时,会携带ids域名下的cookie

ids一看,这用户登陆过,就直接携带用户信息和token并跳转到回调客户端的".../signin-oidc",客户端的后续步骤不变

结尾

本篇只是草草说了各大概,下一篇会先说说ids网页登陆里涉及到的交给核心类,之后会重新按这里的流程走走源码...