从壹开始 [ Ids4实战 ] 之四 ║ 用户数据管理 & 先后端受权联调

前言

 哈喽~~~ 你们周一好!夏天到了,你们舒服了没有,熟话说,战胜你的不是天真,是天真热!😂html

 

过去的一周里,发生了两件事跟你们分享下:前端

①、有两个小伙伴给我提供了 Working Online 的工做,简单说了说,感受应该不太适合,至少我不适合,前期双方试探的成分太多了,我是不喜欢,同时也建议正在找OnLine工做的小伙伴需多多考虑可行性。vue

②、我决定开始《微讲堂》了,具体能够参考右侧公告栏,由于有些基础比较薄弱的小伙伴,单单提供思路仍是没法入门,因此提供在线手把手教学吧,这个我是彻底无所谓,看你心情吧。git

 

这几天经过晚上对 IdentityServer4 的学习和研究,发现这个就是一个“大坑”不是说功能很差,是里边有不少不少的内容须要学习,暂时把开发的 Demo 开放出来了,很简单的,随便看看,以前看官网, 关于 IdentityServer4 的教程,洋洋洒洒就过去了,感受还挺简单,发现要真是落地到项目里了,自我感受又有了压迫感,文末结语中,我简单的说了几点问题,你们能够慢慢往下走,不过知识嘛,无外乎就是本身开心学习 和 本身学习挣钱,这两个心理,加油吧。github

固然平时工做之余,仍是要照顾下先后端分离项目的一些东西的,基础不能丢,主要是三块地方作了修改,这里简单的列一下,就不单独的写文章了,但愿一直在看第一个项目的小伙伴,有缘能够看到吧,不过,就算是看不到也没事儿,遇到了天然就知道了:数据库

一、Blog.Vue 首页的闪屏处理;// 知名博主@张飞洪提出的问题,不知道我是否修改对了;http://vueblog.neters.club/npm

二、Blog.Admin 后台框架调整优化;// ①登陆页样式改版,②Tabs 导航条优化,③兼容手机屏幕等;http://vueadmin.neters.club/后端

三、Blog.Core 后端项目增长 Wiki 页;// 为了让刚接触框架的小伙伴能快速一览,特意在 Github 上,建立了 Wiki ,只不过如今才打了个目录,内容慢慢填,若是还有其余的不足之处,欢迎提建议;https://github.com/anjoy8/Blog.Core/wikiapi

 

忽然转话题,上次我们第一次对项目进行持久化操做《三║ 详解受权持久化 & 用户数据迁移》,不知道小伙伴都看了多少,这里再把几个重要问题提一下,但愿不要忘记了才好:浏览器

1、Ids4 一共用到了几个上下文,分别的用处是什么?

2、在迁移中,数据库生成了多少表,各个模块又是干什么的?

3、Ids4 的良好扩展性,体如今哪里?丰富性又体如今哪里?

4、ApplicationUser 类是起到什么做用的?

 

若是脑子里有些东西,那就恭喜了,若是第一次看,或者彻底不知道我在说什么的话,请看上一集,今天会说说我在研究的过程当中,遇到的两个 Flag 🚩,也就是两个问题,但愿有心的小伙伴,能够帮忙思考下,欢迎找我讨论,废话很少说,开车,立刻讲解今天的内容!🛴🚄

 

 

零、今天要实现绿色的部分

 

 (知识结构图,注意这是我本身的讲解结构,和Ids4知识图解无关)

 

 

 

1、用户数据处理 —— Identity

我们在上篇文章中,简单的将 IdentityServer4 的结构进行持久化处理,并把先后端项目中的用户数据进行迁移处理,最后修改了登陆页的样式,基本知足了登陆和登出的操做,做为一个受权服务中心,仅仅只有登陆是彻底解决不了什么问题的,至少应该对用户数据进行常规操做处理,好比 CURD 等基础操做。

正好,咱们使用了 NetCore 自带的 Identity 机制,能够帮助咱们作一部分工做,由于它本身也封装了一些方法,咱们能够根据他们的方法,实当的作些扩展,从而达到相应的目的,具体有哪些操做,请往下看:

 

一、用户数据展现(有权限)

 既然有数据处理,确定得有展现出来,固然,这个不是必定的,只是作下处理,若是你担忧会有数据安全问题的话,要么不显示数据,要么只显示无关痛痒的两列,甚至能够直接加上权限,只有超级管理员或者技术人员能够看到就行。我这里仅仅是加了个登陆权限,只有登陆的用户才能看的到:

 

// 注入用户管理
private readonly UserManager<ApplicationUser> _userManager;


 [HttpGet]
 [Route("account/users")]
 [Authorize]//能够自定义规则 public IActionResult Users(string returnUrl = null)
 {
     ViewData["ReturnUrl"] = returnUrl;
     var users = _userManager.Users.Where(d => !d.tdIsDelete).OrderBy(d => d.UserName).ToList();//Identity 已经对内部的一些方法作了封装,直接使用便可,若是你对 Net 自带的 Identity 使用过的话,应该很容易上手。

     return View(users);
 }

 

注意下上边的红色标注的地方,下文会说到为啥这里用到了 isDelete 。

咱们简单的对 User 页面作了受权处理,必须登陆状态下才能有权访问,若是是没有登陆,会直接跳转到登陆页面:

 (带权限的用户展现页)

 

二、注册

 关于注册其实咱们以前已经说过了,为何呢,由于咱们在以前导入用户数据的时候,就已经用到了这个方法,只不过这里单拎出来了,可是这里有一个问题须要咱们好好的思考思考,那就是角色的获取!这里就是我下边要说的第一个“Flag”🚩,为何重要呢,不知道如今读的你是否使用过 IdentityServer4 ,我也这几天在考虑这个问题,受权中心确定须要有用户管理的,那很天然的,就会出现 “ 区分控制 ” 的问题,这里简单说下会出现的两个状况:

一、前台展现项目:若是咱们的vue 项目,是一个前台网站,好比 电商类 的或者 Blog.Vue 这样的,很简单,咱们只须要在 api 上加上 [Authorize] 这个无具体规则的受权特性就行,你们先不要往下看,先停一分钟想想是否是这个状况。商城嘛,只须要用户登陆一下就能够购买了,咱们不须要特意的区分商城用户有什么区别,有什么三六九等,你们都是同样,登陆了,就能够任何操做,不管是买东西,仍是写文章,亦或者投票等等;

二、后台管理项目:可是!还有另外一种状况,那就是后台管理,一个对用户身份要求特别严格的一个系统,咱们确定不能仅仅在 api 接口地址上,加上 [Authorize] 这个简单的特性就完事儿了,就好比咱们的 Blog.Admin 项目,确定须要一套复杂的受权策略机制,那就不得不用到用户的角色信息,或者其余的模块信息,这就是我上边说的 “区分控制”;(至因而基于角色的策略,仍是模块化,我还在考虑中,目前先尝试角色管理

三、猜测:你是否是想说使用基于角色+策略受权的 Hybrid Flow 混合模式?别着急,之后的问题会说到,这里提出这个问题,就是向给你们一个思路的过程。

 

若是是第二种状况的话,咱们在用户注册的时候,就须要带上 “角色” 这个信息,好比我这里先默认是一个 test 系统测试管理员的角色这个暂时这么处理,后期我会再深刻研究下,是否是这个模式,或者若是正再看的你很懂的话,欢迎指导下,不胜感激!)固然,若是你的项目不须要对用户的权限进行划分,就好比我上边的第一种状况,电商类,博客类,只要不是后台管理这种的前台系统,都很简单,只须要在 api 上加上 [Authorize] ,而后受权中心是不须要角色这个概念的。

咱们学术讨论嘛,固然是从复杂的着手,就把角色给考虑进去了,如今先写死一个角色,咱们之后的文章中会进一步讨论这个复杂的状况:

 [HttpPost]
 [Route("account/register")]
 [ValidateAntiForgeryToken]
 public async Task<IActionResult> Register(RegisterViewModel model, string returnUrl = null, string rName = "AdminTest")
        {
            ViewData["ReturnUrl"] = returnUrl;
            IdentityResult result = new IdentityResult();
            // 模型校验
            if (ModelState.IsValid)
            {
                // 判断用户名是否存在,说明:若是是DDD设计思想,这中查重应该是写在领域模型的。
                var userItem = _userManager.FindByNameAsync(model.LoginName).Result;
                if (userItem == null)
                {
                    // 转成咱们的实体模型,说明:这种多个实体转换,可使用 Dto
                    var user = new ApplicationUser
                    {
                        Email = model.Email,
                        UserName = model.LoginName,
                        LoginName = model.RealName,
                        sex = model.Sex,
                        age = model.Birth.Year - DateTime.Now.Year,
                        birth = model.Birth,
                        addr = "",
                        tdIsDelete = false
                    };
                    // 建立用户,注意密码的规范,好比必须有大小写字母+数字+符号
                    result = await _userManager.CreateAsync(user, model.Password);
                    if (result.Succeeded)
                    {
                        // 用户添加成功后,就须要添加声明了,看本身须要多少吧,能够自定义扩展
                        result = await _userManager.AddClaimsAsync(user, new Claim[]{
                            // 这个 Name ,就是 Jwt 的惟一名字,也是页面里展现的名称,好比是“测试帐号”,而不是登陆名的“test1”
                            new Claim(JwtClaimTypes.Name, model.RealName),
                            new Claim(JwtClaimTypes.Email, model.Email),
                            // 是否须要进行 Email 邮件验证
                            new Claim(JwtClaimTypes.EmailVerified, "false", ClaimValueTypes.Boolean),
                            // 这里就是角色声明
                            new Claim(JwtClaimTypes.Role, rName)
                        });
                        if (result.Succeeded)
                        {
                            // 添加成功,能够直接登陆,这个就好比是咱们的博客项目或者电商项目,咱们在受权中心注册成功后,直接登陆了,跳转到前台了。
                            //await _signInManager.SignInAsync(user, isPersistent: false);
                            return RedirectToLocal(returnUrl);
                        }
                    }
                }
                else
                {
                    ModelState.AddModelError(string.Empty, $"{userItem?.UserName} already exists");
                }
                // 收集所有异常数据,返回前台
                AddErrors(result);
            }
            return View(model);
        }

 

上边的就是注册的主要代码,你们能够本身任意的扩展,而后重要的部分,我已经标红,也写上了详细的注释,特别简单,都能看懂。

 

这一 Part 都很日常,最重要的一个问题仍是那个角色这一块,但愿读到这里的都能看懂,想想到底你的项目里需不须要这样的 Claim,不懂的欢迎来讨论。

 

三、更新 与 逻辑删除(有权限)

上边我们说到了展现和添加,那下边就是说到更新了(这个操做我带上了最高的权限,必须是超级管理员才能操做 [Authorize(Roles = "SuperAdmin")] ),你会问,为啥要把删除和更新放到一块儿呢?其实我我的感受逻辑是同样的,平时开发确定也都知道,逻辑删除其实就是把“是否删除” 这个字段设置成 True 就好了,可是真的是这样么,咱们慢慢往下看。

 首先更新用户这个很简单的,我就很少说什么了,具体的能够看看代码,主要的逻辑就是平时的三步走:

一、查询出当前人Model;

二、用视图模型修改Model;

三、执行更新操做  _userManager.UpdateAsync(userItem); // 这里要说下就是,Identity 自带了不少扩展方法,你们须要本身好好的研究下,从而达到本身的相应目的。

 

更新说完了,下边说说删除,删除其实自己就有两种状况:

一、逻辑删除,很天然,就是将数据更新下状态,好比咱们能够用上边的方法,把当前操做人的 IsDeleted=True 便可,很简单;

二、物理删除,这个仍是须要好好研究研究,我在官方的代码里,没有找到如何物理删除的方法,可能仍是须要开发者本身定义扩展吧;

 

 这就是我说的第二个 “Flag”🚩 ,须要好好的思考思考,若是你已经忘了第一个 Flag 的话,请向上看,用户注册章节里的角色问题。

 

 

(更新 & 删除 有权限 动图)

 

 

四、重置密码

 这个是目前为止稍微复杂一点的,需用用到流程,首先看动图吧:

 

(重置/更新密码 动图)

 

这个过程其实很简单,也是项目中必须使用到的功能,我相信任何一个网站,必需要用到这个重置和找回密码的功能吧,固然生产环境很复杂,可能须要邮箱或者手机等来处理动态连接,我这里只是提供一个思路,总结来讲,流程说明以下:

一、输入当时注册邮箱;

二、获取包含动态 Code 的安全连接(可经过发邮件的形式);

三、根据安全连接,设置新密码;

四、从新登陆;

 

核心代码(节选):

 

// 一、判断邮箱
var user = await _userManager.FindByEmailAsync(model.Email);

// 二、生成重置密码回调连接
var code = await _userManager.GeneratePasswordResetTokenAsync(user);
var callbackUrl = Url.ResetPasswordCallbackLink(user.Id, code, Request.Scheme);
var ResetPassword = $"Please reset your password by clicking here: <a href='{callbackUrl}'>link</a>";

// 三、重置密码
var result = await _userManager.ResetPasswordAsync(user, model.Code, model.Password);

 

 

五、其余状况处理?

经过上边的简单说明,AccountController 这个控制器的内容, 我们说完了,是否是就没有问题了呢,不是!咱们要研究,就要研究透彻,你们确定注意到了这个项目中,基本都说到了,可是在核心的快速启动文件夹 Quickstart 中,还有几个控制器没有说到:

 

 不光如此,在平时的开发中,咱们还会遇到下边这几个业务逻辑操做:

1、如何找回注册邮箱?

2、如何经过发送邮件,从而达到邮件确认的目的?

3、如何实现FaceBook、Google登陆?

4、如何更新用户的角色等Claims?

五、如何刷新 Token ?

 

上边红框中的那几个控制器都是什么意思?

下边四条业务逻辑又该如何实现?

当前项目是否是还有其余不为咱们知道的秘密?之后的章节再慢慢展开,请关注。

 

不过咱们既然已经完成用户的基本操做,咱们就先停下上边的疑惑问题,往下走走,看看 IdentityServer4 究竟是如何经过 OpenID Connect 来操做的。

 

2、简单受权模式 —— Implicit Flow OpenID

0、OpenID Connect受权模式

OPID 认证流程主要是由 OAuth2 的五种受权流程延伸而来的,它有如下 3 种:

  • Authorization Code Flow(受权码模式):基于OAuth2的受权码来换取Id Token和Access Token。
  • Implicit Flow(简化模式):基于OAuth2的Implicit流程获取Id Token和Access Token。
  • Hybrid Flow:混合Authorization Code Flow+Implici Flow获取Id Token和Access Token。

注:OpenID Connect 为何没有基于OAuth2的Resource Owner Password Credentials Grant和Client Credentials Grant扩展,Resource Owner Password Credentials Grant是须要应用提供帐号密码的,帐号密码都有了在获取Id Token意义不大。Client Credentials Grant没有用户的参与因此获取Id Token 也没意义。这也能反映受权和认证的差别,以及只使用OAuth2来作身份认证的事情是远远不够的,也是不合适的。

 

一、概念

简化模式用于获取访问令牌(但它不支持令牌的刷新,之因此因此称为简化模式,和受权码模式比少了获取受权码的步骤),并对运行特定重定向URI的公共客户端进行优化,而这一些列操做一般会使用脚本语言在浏览器中完成,令牌对访问者是可见的,且客户端也不须要验证。

 

简化模式,主要有下边三个特色:

一、用于“公共”客户端;

二、客户端应用直接从浏览器访问资源;

三、没有显式的客户端身份认证;

 

二、结构图

 

为了配合你们理解,我这里有两个场景,你们脑子里先有个画面,而后往下看四个角色和流程图:

场景一:博客园登陆,须要获取腾讯的某一个QQ用户的头像和昵称等资源;

场景二:先后端分离,Vue 项目须要获取 Core 项目的 当前test1帐号的 数据;

 

首先先理解下四个角色:

一、Resource Owner(资源拥有者) —— 资源全部者,就好比咱们受权登陆中的,QQ用户,他才是资源的拥有者。3143422472 / test1帐号

二、Resource Server(资源服务器) —— 资源服务器,用来存储用户资源(头像,昵称等)的服务器,好比腾讯QQ。腾讯QQ服务器 / Blog.Core 

三、Client(客户端) —— 第三方客户端,好比博客园;https://www.cnblogs.com / Blog.Vue

四、Authorization Server(受权服务器)—— 受权服务器,用来做为认证第三方平台的服务,好比腾讯的QQ互联平台。https://graph.qq.com/oauth2.0/show?whic...... / Blog.Idp

 

而后我们看看具体的流程是怎样的:

 

(流程1:参考网上画的,可能不是很明了)

 

 (流程2:本身根据官网图片作了下修改)

 

Tips:Web-Hosted Client Resource 服务器至关因而一个存储 accessToken 的地方,一般指浏览器中的存储(cookie、localStorage、SessionStorge、js变量等),通常这个页面是看不到的,并且通常状况是和 Client 客户端写在一块儿的,固然也有分开的。

 

步骤解析:

  1. 客户端携带客户端标识以及重定向URI到受权服务器;
  2. 用户确认是否要受权给客户端;
  3. 受权服务器获得许可后,跳转到指定的重定向地址,并将令牌也包含在了里面;
  4. 客户端不携带上次获取到的包含令牌的片断,去请求资源服务器;
  5. 资源服务器会向浏览器返回一个脚本;
  6. 浏览器会根据上一步返回的脚本,去提取在C步骤中获取到的令牌;
  7. 浏览器将令牌推送给客户端。

(A步骤)中须要用到的参数,注意在这里要使用"application/x-www-form-urlencoded"格式:

  • response_type 必选项,此值必须为"token" 
  • client_id 必选项
  • redirect_uri 可选项
  • scope 可选项
  • state 建议选项

    例如:

&redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb HTTP/1.1GET /authorize?response_type=token&client_id=s6BhdRkqt3&state=xyz
 

    (C步骤)中返回的参数包含:

  • access_token 必选项
  • token_type 必选项
  • expires_in 建议选项
  • scope 可选项
  • state 必选项

    例如:

HTTP/1.1 302 Found      Location: http://example.com/cb#access_token=2YotnFZFEjr1zCsicMWpAA&state=xyz&token_type=example&expires_in=3600

 

上边咱们简单的说了说 Implicit Flow 模式的相关知识点,不知道你们有没有一点点感受,若是不是很懂,正好感受配合着下边的代码研究下,两者结合会更好。

  

3、先后端项目受权联调

 

 由于咱们用到了先后端分离项目,因此必定是要三方处理,若是你如今使用的是 MVC 模式的话,咱们之后的章节也会说到 受权码受权模式(Authorization Code Flow),这里先把简化模式调通了:

一、受权服务端 —— Implicit(Blog.Idp)

 这个配置很简单,在 Blog.Idp 项目中,你们别看是在 Config.cs 文件里,其实它已经在咱们上一篇文章中,生成到了数据库中,不懂的请回看上一篇文章

 new Client {
     ClientId = "blogvuejs",//客户端id
     ClientName = "Blog.Vue JavaScript Client",
     AllowedGrantTypes = GrantTypes.Implicit,
     AllowAccessTokensViaBrowser = true,

     RedirectUris =           { "http://localhost:6688/callback" },//回调页面
     PostLogoutRedirectUris = { "http://localhost:6688" },
     AllowedCorsOrigins =     { "http://localhost:6688" },
 // 容许的前端获取的做用域
     AllowedScopes = {
         IdentityServerConstants.StandardScopes.OpenId,
         IdentityServerConstants.StandardScopes.Profile,
         "roles",
         "blog.core.api"
     }
 }

 

 

 

 

 

二、资源服务端 —— Bearer(Blog.Core)

 

这里的配置是在 Blog.Core 咱们的资源服务器中,在启动文件 Startup.cs 中,你们自行查看,注意若是使用这个的话,请把 Jwt 认证给注释掉:

services.AddAuthentication("Bearer")
  .AddIdentityServerAuthentication(options =>
  {
      options.Authority = "http://localhost:5002";//受权服务器地址
      options.RequireHttpsMetadata = false;//是否Https
      options.ApiName = "blog.core.api";//咱们在 Blog.Idp 中配置的资源服务器名
  });

 

 

添加过程当中,可能会须要引用扩展包 : IdentityServer4.AccessTokenValidation 这都是小问题,你们自行检查便可。 

 

三、请求客户端 —— Oidc(Blog.Vue)

上边咱们已经在两个服务端作好了配置,客户端如何处理,这个地方才是今天的重头戏,不管是什么客户端,JS 或者 Vue、React、Ng 等等前端框架,都须要用到 oidc-client 这个插件库:

 

一、安装

执行命令:npm install oidc-client --save

 

二、封装

注意这个是一个js库,咱们就像以前将 SignalR 那样,直接使用就行,不用在 main.js 中引用,可是仍是须要先实例化一个用户管理类 ApplicationUserManager  并配置构造函数,请注意这些参数都要和 Blog.Idp 受权服务器配置一致。

在 src 文件夹下 新建 Auth 文件夹,并添加 applicationusermanager.js 来封装咱们的链接管理:

import { UserManager } from 'oidc-client'

class ApplicationUserManager extends UserManager {
  constructor () {
    super({
      authority: 'http://localhost:5002',// 受权服务中心地址
      client_id: 'blogvuejs',// 客户端 id
      redirect_uri: 'http://localhost:6688/callback',// 登陆回调地址
      response_type: 'id_token token',
      scope: 'openid profile roles blog.core.api',// 做用域也要一一匹配
      post_logout_redirect_uri: 'http://localhost:6688' //登出后回调地址
    })
  }

  async login () {
    await this.signinRedirect()
    return this.getUser()
  }

  async logout () {
    return this.signoutRedirect()
  }
}

 

同时为了配合其余页面使用,咱们封装几个经常使用的方法,在 Auth 文件夹下,新建 UserAuth.js 来封装用户的一些基本信息:

import applicationUserManager from "./applicationusermanager";
const userAuth = {
  data() {
    return {
      user: {
        name: "",
        isAuthenticated: false
      }
    };
  },
  methods: {
    async refreshUserInfo() {//获取用户信息 const user = await applicationUserManager.getUser();
      if (user) {
        this.user.name = user.profile.name;
        this.user.isAuthenticated = true;
      } else {
        this.user.name = "";
        this.user.isAuthenticated = false;
      }
    }
  },
  async created() {
    await this.refreshUserInfo();
  }
};
export default userAuth;

 

 

三、发起 登陆/登出 请求

咱们封装好了方法,下边就是直接设计业务逻辑了,过程很简单,在 App.vue 组件中:

 

一、每次路由跳转需异步获取用户数据;

二、发起异步登陆请求;

三、发起异步登出请求;

 

import applicationUserManager from "./Auth/applicationusermanager";
import userAuth from "./Auth/UserAuth";

export default {
  name: "app",
  mixins: [userAuth],
  data: function() {
    return {};
  },
  watch: {
    $route: async function(to, from) {
      //这里使用Id4受权认证,用Jwt,请删之;
      // await this.refreshUserInfo();
    }
  },
  methods: {
    async login() {
      try {
        await applicationUserManager.login();
      } catch (error) {
        console.log(error);
        this.$root.$emit("show-snackbar", { message: error });
      }
    },
    async logout() {
      try {
        await applicationUserManager.logout();
        this.$store.commit("saveToken", "");
      } catch (error) {
        console.log(error);
        this.$root.$emit("show-snackbar", { message: error });
      }
    }
  }
};

 

 

四、回调

在上边的用户管理配置中,咱们用到了一个回调页面,这个很重要,由于咱们在登陆成功后,须要调整到客户端,而且须要将信息给存储下来,就是上边流程图中,咱们说到的 客户端资源

 

 具体怎么写的,很简单,在 views 视图页面文件夹下,新建一个 LoginCallbackView.vue 页面:

import applicationUserManager from '../Auth/applicationusermanager'

export default {
  async created () {
    try {
      // 核心的就是这里了
      await applicationUserManager.signinRedirectCallback()
        let user = await applicationUserManager.getUser()
        // 将 token 存储在客户端
        this.$store.commit("saveToken", user.access_token);
        // 调整首页
        this.$router.push({name: 'home'})
    } catch (e) {
      console.log(e)
      this.$root.$emit('show-snackbar', { message: e })
    }
  }
}

 

 

 

4、结语

 

本文仍是延续上篇文章的快速讲解的风格,简单连贯的把用户管理和先后端联调的内容通了一遍,总结一下:


一、分析了用户是否须要角色等策略的原因;

二、实现了对用户的基本操做——CURD+重置密码;

三、受权项目中还遗留了一片未知的知识块,亟待探索;

四、实现了客户端、资源服务器、受权服务器的第一次联调;

五、重点讲解了五大模式中的 Implicit Flow 简化模式的概念和应用场景;

六、同时也把 Hybrid Flow 混合模式给引伸出来,由于它基于 角色+策略 的受权;

 

固然,经过这一篇的学习,又开拓出了更多的未知领域,IdentityServer4 没有咱们想一想的那么难,可是确定也不是一个 Demo 就能说的完的简单,

如何解决文章中提到的,打算提到的,未提到的各类问题呢,请持续关注吧。

 

 

5、Github && Gitee

 https://github.com/anjoy8/Blog.IdentityServer

相关文章
相关标签/搜索