• IdentityServer4网页(单点)登陆入门


    前言

    本篇说说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网页登陆里涉及到的交给核心类,之后会重新按这里的流程走走源码...

  • 相关阅读:
    2015-01-27-从实验出发理解buffer与cache区别-吴伟顺
    【实习记】2014-09-26恢复linux下误删的ntfs盘中的文件
    【实习记】2014-09-24万事达卡bin查询项目总结
    【实习记】2014-09-04浏览代码查middle资料+总结我折腾过的源码浏览器
    【实习记】2014-09-01从复杂到简单:一行命令区间查重+长整型在awk中的bug
    【实习记】2014-08-29算法学习Boyer-Moore和最长公共子串(LCS)
    【实习记】2014-08-28知值求范围问题
    【实习记】2014-08-27堆排序理解总结+使用typedef指代函数指针
    【实习记】2014-08-26都是回车惹的祸——shell脚本必须是unix行尾
    【实习记】2014-08-24实习生无法映射磁盘替代方案rsync+非默认端口22设置
  • 原文地址:https://www.cnblogs.com/jionsoft/p/13545273.html
Copyright © 2020-2023  润新知