任务23:Consent 代码重构

新建一个 Sercices 文件夹,在文件夹下新建一个 ConsentService,专门用于处理 Consent 的逻辑,我们会把 controller 中不是 action 的方法移到 service 中

先将 ConsentController 私有变量和构造函数搬到 ConsentService 中

ConsentService

  1. private readonly IClientStore _clientSotre;
  2. private readonly IResourceStore _resourceStore;
  3. private readonly IIdentityServerInteractionService _identityServerInteractionService;
  4. public ConsentService(
  5. IClientStore clientStore,
  6. IResourceStore resourceStore,
  7. IIdentityServerInteractionService identityServerInteractionService)
  8. {
  9. _clientSotre = clientStore;
  10. _resourceStore = resourceStore;
  11. _identityServerInteractionService = identityServerInteractionService;
  12. }

接着搬移私有方法,并将 BuildConsentViewModel 修改为 public

ConsentService

  1. #region Private Methods
  2. private ConsentViewModel CreateConsentViewModel(AuthorizationRequest request, Client client, Resources resources)
  3. {
  4. var vm = new ConsentViewModel();
  5. vm.ClientName = client.ClientName;
  6. vm.ClientLogoUrl = client.LogoUri;
  7. vm.ClientUrl = client.ClientUri;
  8. vm.RememberConsent = client.AllowRememberConsent;
  9. vm.IdentityScopes = resources.IdentityResources.Select(i => CreateScopeViewModel(i));
  10. vm.ResourceScopes = resources.ApiResources.SelectMany(i => i.Scopes).Select(x => CreateScopeViewModel(x));
  11. return vm;
  12. }
  13. private ScopeViewModel CreateScopeViewModel(IdentityResource identityResource)
  14. {
  15. return new ScopeViewModel
  16. {
  17. Name = identityResource.Name,
  18. DisplayName = identityResource.DisplayName,
  19. Description = identityResource.Description,
  20. Required = identityResource.Required,
  21. Checked = identityResource.Required,
  22. Emphasize = identityResource.Emphasize,
  23. };
  24. }
  25. private ScopeViewModel CreateScopeViewModel(Scope scope)
  26. {
  27. return new ScopeViewModel
  28. {
  29. Name = scope.Name,
  30. DisplayName = scope.DisplayName,
  31. Description = scope.Description,
  32. Required = scope.Required,
  33. Checked = scope.Required,
  34. Emphasize = scope.Emphasize,
  35. };
  36. }
  37. #endregion
  38. public async Task<ConsentViewModel> BuildConsentViewModel(string returnUrl)
  39. {
  40. var request = await _identityServerInteractionService.GetAuthorizationContextAsync(returnUrl);
  41. if (request == null)
  42. return null;
  43. var client = await _clientSotre.FindEnabledClientByIdAsync(request.ClientId);
  44. var resources = await _resourceStore.FindEnabledResourcesByScopeAsync(request.ScopesRequested);
  45. var vm = CreateConsentViewModel(request, client, resources);
  46. vm.ReturnUrl = returnUrl;
  47. return vm;
  48. }

接着将 ConsentService 注入到 ConsentController 中并调用 BuildConsentViewModel

ConsentController

  1. private readonly ConsentService _consentService;
  2. public ConsentController(ConsentService consentService)
  3. {
  4. _consentService = consentService;
  5. }
  6. [HttpGet]
  7. public async Task<IActionResult> Index(string returnUrl)
  8. {
  9. var model = await _consentService.BuildConsentViewModel(returnUrl);
  10. if (model == null)
  11. {
  12. }
  13. return View(model);
  14. }

接着将 ConsentController 中 post 的逻辑搬到 ConsentService 的一个方法 ProcessConsent 中

这里不能直接调用 Redirect 所以需要一个新建一个ViewModel 作为返回

ProcessConsentResult

  1. public class ProcessConsentResult
  2. {
  3. public string RedirectUrl { get; set; }
  4. public bool IsRedirect => RedirectUrl != null;
  5. }

ConsentService

  1. public async Task<ProcessConsentResult> ProcessConsent(InputConsentViewModel viewModel)
  2. {
  3. ConsentResponse consentResponse = null;
  4. var result = new ProcessConsentResult();
  5. if (viewModel.Button == "no")
  6. {
  7. consentResponse = ConsentResponse.Denied;
  8. }
  9. else if (viewModel.Button == "yes")
  10. {
  11. if (viewModel.ScopesConsented != null && viewModel.ScopesConsented.Any())
  12. {
  13. consentResponse = new ConsentResponse
  14. {
  15. RememberConsent = viewModel.RememberConsent,
  16. ScopesConsented = viewModel.ScopesConsented,
  17. };
  18. }
  19. }
  20. if (consentResponse != null)
  21. {
  22. var request = await _identityServerInteractionService.GetAuthorizationContextAsync(viewModel.ReturnUrl);
  23. await _identityServerInteractionService.GrantConsentAsync(request, consentResponse);
  24. result.RedirectUrl = viewModel.ReturnUrl;
  25. }
  26. return result;
  27. }

接着在 ConsentController 的 post 逻辑中调用 ProcessConsent

ConsentController

  1. [HttpPost]
  2. public async Task<IActionResult> Index(InputConsentViewModel viewModel)
  3. {
  4. var result = await _consentService.ProcessConsent(viewModel);
  5. if (result.IsRedirect)
  6. {
  7. return Redirect(result.RedirectUrl);
  8. }
  9. return View(viewModel);
  10. }

因为在视图层 index 中使用的是 ConsentViewModel,不能直接把 InputConsentViewModel 传过去,因为是无法识别的,所以我们需要在 ConsentService 中转换一下

首先在 ProcessConsentResult 中添加一个 ConsentViewModel

ProcessConsentResult

  1. public class ProcessConsentResult
  2. {
  3. public string RedirectUrl { get; set; }
  4. public bool IsRedirect => RedirectUrl != null;
  5. public ConsentViewModel viewModel { get; set; }
  6. }

在什么情况下会返回这个 ViewModel,当 ConsentService 的 ProcessConsent 方法中的 consentResponse 为 null 的时候,在这个时候我们需要给它封装一个 model

ConsentService

  1. if (consentResponse != null)
  2. {
  3. ...
  4. }
  5. {
  6. var consentViewModel = await BuildConsentViewModel(viewModel.ReturnUrl);
  7. result.viewModel = consentViewModel;
  8. }

但是在 BuildConsentViewModel 的时候,ConsentViewModel 的 ScopeViewModel 里面有 Required 和 Checked,如果在填写的时候已经勾选了,我们需要把它的状态带过去,而在 viewModel.ScopesConsented 的时候已经知道勾选了哪些,所以我们需要把 model 传过去

ConsentService

  1. var consentViewModel = await BuildConsentViewModel(viewModel.ReturnUrl, viewModel);

改造一下 BuildConsentViewModel,接收一个 InputConsentViewModel,默认为 null,如有它有值,可以知道客户的选中信息,然后传入 CreateConsentViewModel 中

ConsentService

  1. public async Task<ConsentViewModel> BuildConsentViewModel(string returnUrl, InputConsentViewModel model = null)
  2. {
  3. ...
  4. var vm = CreateConsentViewModel(request, client, resources, model);
  5. ...
  6. }

所以在 CreateConsentViewModel 的时候对 Checked 赋值,或者已经选中的情况下就选中

ConsentService

  1. private ScopeViewModel CreateScopeViewModel(IdentityResource identityResource, bool check)
  2. {
  3. return new ScopeViewModel
  4. {
  5. Name = identityResource.Name,
  6. DisplayName = identityResource.DisplayName,
  7. Description = identityResource.Description,
  8. Required = identityResource.Required,
  9. Checked = check || identityResource.Required,
  10. Emphasize = identityResource.Emphasize,
  11. };
  12. }
  13. private ScopeViewModel CreateScopeViewModel(Scope scope, bool check)
  14. {
  15. return new ScopeViewModel
  16. {
  17. Name = scope.Name,
  18. DisplayName = scope.DisplayName,
  19. Description = scope.Description,
  20. Required = scope.Required,
  21. Checked = check ||scope.Required,
  22. Emphasize = scope.Emphasize,
  23. };
  24. }

接着处理一下 CreateConsentViewModel,传入一个 InputConsentViewModel,然后修改 RememberConsent,以及获取一个 selectedScopes

ConsentService

  1. private ConsentViewModel CreateConsentViewModel(AuthorizationRequest request, Client client, Resources resources, InputConsentViewModel model)
  2. {
  3. var rememberConsent = model?.RememberConsent ?? true;
  4. var selectedScopes = model?.ScopesConsented ?? Enumerable.Empty<string>();
  5. var vm = new ConsentViewModel();
  6. vm.ClientName = client.ClientName;
  7. vm.ClientLogoUrl = client.LogoUri;
  8. vm.ClientUrl = client.ClientUri;
  9. vm.RememberConsent = rememberConsent;
  10. vm.IdentityScopes = resources.IdentityResources.Select(i => CreateScopeViewModel(i, selectedScopes.Contains(i.Name) || model == null));
  11. vm.ResourceScopes = resources.ApiResources.SelectMany(i => i.Scopes).Select(x => CreateScopeViewModel(x, selectedScopes.Contains(x.Name) || model == null));
  12. return vm;
  13. }

这一块就改进完了,接下来就是在不选中的情况下会有提示让我们选择,现在添加一些错误的提示,在 ProcessConsentResult 中添加一些信息

ProcessConsentResult

  1. public string ValidationError { get; set; }

赋值 ValidationError

ConsentService

  1. else if (viewModel.Button == "yes")
  2. {
  3. if (viewModel.ScopesConsented != null && viewModel.ScopesConsented.Any())
  4. {
  5. consentResponse = new ConsentResponse
  6. {
  7. RememberConsent = viewModel.RememberConsent,
  8. ScopesConsented = viewModel.ScopesConsented,
  9. };
  10. }
  11. result.ValidationError = "请至少选中一个权限";
  12. }

接着处理一下页面,将信息返回

ConsentController

  1. [HttpPost]
  2. public async Task<IActionResult> Index(InputConsentViewModel viewModel)
  3. {
  4. var result = await _consentService.ProcessConsent(viewModel);
  5. if (result.IsRedirect)
  6. {
  7. return Redirect(result.RedirectUrl);
  8. }
  9. if (!string.IsNullOrEmpty(result.ValidationError))
  10. {
  11. ModelState.AddModelError("", result.ValidationError);
  12. }
  13. return View(result.viewModel);
  14. }

加入验证信息之后需要修改视图把这块信息显示出来

Index

  1. <input type="hidden" asp-for="ReturnUrl"/>
  2. <div class="alert alert-danger">
  3. <strong>Error""</strong>
  4. <div asp-validation-summary="All" class="danger"></div>
  5. </div>

添加依赖注入

startup

  1. services.AddScoped<ConsentService>();

启动服务端,启动客户端

因为默认勾选第一个,无法看到错误信息,所以去掉 disabled 与 hidden 控件,两个选项都不勾选,点击同意就会看到错误信息

_ScopeListitem

  1. disabled="@Model.Required"
  2. @if (Model.Required)
  3. {
  4. <input type="hidden" name="ScopesConsented" value="@Model.Name"/>
  5. }

课程链接

http://video.jessetalk.cn/course/explore

本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。

欢迎转载、使用、重新发布,但务必保留文章署名 郑子铭 (包含链接: http://www.cnblogs.com/MingsonZheng/ ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。

如有任何疑问,请与我联系 (MingsonZheng@outlook.com) 。

ASP.NET Core分布式项目实战(Consent 代码重构)--学习笔记的更多相关文章

  1. ASP.NET Core分布式项目实战

    ASP.NET Core开发者成长路线图 asp.net core 官方文档 https://docs.microsoft.com/zh-cn/aspnet/core/getting-started/ ...

  2. 【笔记目录1】ASP.NET Core分布式项目实战

    当前标签: ASP.NET Core分布式项目实战 共2页: 1 2 下一页  35.Docker安装Mysql挂载Host Volume GASA 2019-06-20 22:02 阅读:51 评论 ...

  3. 【笔记目录2】ASP.NET Core分布式项目实战

    当前标签: ASP.NET Core分布式项目实战 共2页: 上一页 1 2  11.ClientCredential模式总结 GASA 2019-03-11 12:59 阅读:26 评论:0 10. ...

  4. 【ASP.NET Core分布式项目实战】(三)整理IdentityServer4 MVC授权、Consent功能实现

    本博客根据http://video.jessetalk.cn/my/course/5视频整理(内容可能会有部分,推荐看源视频学习) 前言 由于之前的博客都是基于其他的博客进行开发,现在重新整理一下方便 ...

  5. ASP.NET Core分布式项目实战-目录

    前言 今年是2018年,发现已经有4年没有写博客了,在这4年的时光里,接触了很多的.NET技术,自己的技术也得到很大的进步.在这段时光里面很感谢张队长以及其他开发者一直对.NET Core开源社区做出 ...

  6. 【ASP.NET Core分布式项目实战】(六)Gitlab安装

    Gitlab GitLab是由GitLabInc.开发,使用MIT许可证的基于网络的Git仓库管理工具,且具有wiki和issue跟踪功能.使用Git作为代码管理工具,并在此基础上搭建起来的web服务 ...

  7. 【ASP.NET Core分布式项目实战】(一)IdentityServer4登录中心、oauth密码模式identity server4实现

    本博客根据http://video.jessetalk.cn/my/course/5视频整理 资料 OAuth2 流程:http://www.ruanyifeng.com/blog/2014/05/o ...

  8. 【ASP.NET Core分布式项目实战】(五)Docker制作dotnet core控制台程序镜像

    Docker制作dotnet core控制台程序镜像 基于dotnet SDK 新建控制台程序 mkdir /home/console cd /home/console dotnet new cons ...

  9. 【ASP.NET Core分布式项目实战】(二)oauth2 + oidc 实现 server部分

    本博客根据http://video.jessetalk.cn/my/course/5视频整理(内容可能会有部分,推荐看源视频学习) 资料 我们基于之前的MvcCookieAuthSample来做开发 ...

  10. 【ASP.NET Core分布式项目实战】(四)使用mysql/mysql-server安装mysql

    Docker安装Mysql 拉取镜像 docker pull mysql/mysql-server 运行mysql docker run -d -p : --name mysql01 mysql/my ...

随机推荐

  1. 【驱动】ifconfig up后内核网络驱动做了什么.md

    背景 最近在排查一个网络问题,ifconfig eth0 up 后,网卡link up比较慢.因此,分析了下从ifconfig up 到网络驱动的调用流程.这里顺便作个记录. ifconfig eth ...

  2. 动态给div赋值高,使页面高度100%

    import { ref, onMounted, onUnmounted, computed, nextTick } from 'vue' const boxRef = ref() const sea ...

  3. 机器学习-概率图模型系列-隐含马尔科夫-维特比算法解码隐藏序列-HMM模型参数估计-36

    目录 待补充 参考资料 刘建平博客 pinard

  4. SV 自定义数据类型

    概述 自定义类型 枚举类型 定义枚举值 自定义枚举类型 枚举类型之间进行赋值是可以的 枚举类型可以赋值给整型,整型不能直接赋值给枚举类型 枚举类型 + 1 ==> 会进行隐式的转换,枚举类型转换 ...

  5. jenkins构建报错: Send build artifacts over SSH' changed build result to UNSTABLE

    原因包括: ssh配置的用户没有相关的权限. 最好是配置root用户

  6. java - 字符串转数字

    Integer.valueOf("str").intValue(): Integer.valueOf("123").intValue():

  7. JMS微服务开发示例(三)使用分布式锁和编写定时任务

    分布式锁 在Controller当中,提供了分布式锁的功能,代码如下: class HelloworldController : MicroServiceControllerBase { static ...

  8. [转帖]oceanbase 的简单介绍

    English | 中文版 OceanBase Database 是一个分布式关系型数据库.完全由蚂蚁集团自主研发. OceanBase 基于 Paxos 协议以及分布式架构,实现了高可用和线性扩展. ...

  9. [转帖]k8s(1.28.2)部署ingress-nginx-controller(1.9.0)

    1.部署ingress-nginx-controller 继在三台虚拟机部署k8s后,需要部署ingress-nginx-controller,才能使设置的ingress规则生效. 1.1下载yaml ...

  10. [转帖]lsblk命令详解

    https://www.cnblogs.com/ishmaelwanglin/p/11043918.html lsblk命令用来查看block设备的信息. 主要应用场景: 获取wwnid,获取块设备列 ...