Consent支持

  • 添加一个Consent控制器
  • 实现ConsentService
  • Consent页面的实现
  • Clients修改
    • IdentityServer4源码
    • 本文源码
    • 几个Model

IdentityServer4Consent用于允许终端用户授予客户端对资源的访问权限,通常用于第三方客户端。我们在IdentityServer4(三):基于ASP.NET Core的交互式认证授权中曾使用过用户名密码进行登录,但那种方式通常针对内部的信任应用。假如有一个第三方厂家需要接入我们的认证授权服务,使用我们的用户信息进行登录验证等,我们通常要提供Consent页面。
本文将实现一个类似下图的功能:

照常,先演示效果

以下对第三方客户端MVC测试程序简称为客户端,对认证授权服务简称为平台服务

添加一个Consent控制器

在打开客户端后,客户端调用平台服务(使用平台用户进行登录),在成功输入平台的账号和密码后,将会发生Get请求的Index方法,跳转到Consent页面,此时将访问上图演示的Consent页面。在用户勾选权限,并同意后,将发送Post请求到Index

public class ConsentController : Controller
{private readonly ConsentService _consentService;public ConsentController(ConsentService consentService){_consentService = consentService;}[HttpGet]public async Task<IActionResult> Index(string returnUrl){var vm = await _consentService.BuildConsentViewModelAsync(returnUrl);if (vm == null){return View("Error");}return View(vm);}[HttpPost]public async Task<IActionResult> Index(ConsentInputModel model){var result = await _consentService.ProcessConsentAsync(model);if (result.IsRedirect){return Redirect(result.RedirectUrl);}if (!string.IsNullOrEmpty(result.ValidationError)){ModelState.AddModelError("", result.ValidationError);}return View(result.ViewModel);}
}

实现ConsentService

  • 构造函数注入相关服务
public class ConsentService
{private readonly IClientStore _clientStore;private readonly IResourceStore _resourceStore;private readonly IIdentityServerInteractionService _identityServerInteractionService;public ConsentService(IClientStore clientStore, IResourceStore resourceStore, IIdentityServerInteractionService identityServerInteractionService){_clientStore = clientStore;_resourceStore = resourceStore;_identityServerInteractionService = identityServerInteractionService;}
}
  • 创建CreateConsentViewModel
private ConsentViewModel CreateConsentViewModel(AuthorizationRequest request, Client client, Resources resources, ConsentInputModel model)
{var vm = new ConsentViewModel{ClientName = client.ClientName ?? client.ClientId,ClientLogoUrl = client.LogoUri,ClientUrl = client.ClientUri,AllowRememberConsent = client.AllowRememberConsent,RememberConsent = model?.RememberConsent ?? true,ScopesConsented = model?.ScopesConsented ?? Enumerable.Empty<string>(),};vm.IdentityScopes = resources.IdentityResources.Select(i => CreateScopeViewModel(i, vm.ScopesConsented.Contains(i.Name) || model == null));vm.ResourceScopes = resources.ApiResources.SelectMany(a => a.Scopes).Select(s => CreateScopeViewModel(s, vm.ScopesConsented.Contains(s.Name) || model == null));return vm;
}private ScopeViewModel CreateScopeViewModel(IdentityResource identityResource, bool check)
{return new ScopeViewModel{Name = identityResource.Name,DisplayName = identityResource.DisplayName,Description = identityResource.Description,Emphasize = identityResource.Emphasize,Checked = check || identityResource.Required,Required = identityResource.Required};
}
private ScopeViewModel CreateScopeViewModel(Scope scope, bool check)
{return new ScopeViewModel{Name = scope.Name,DisplayName = scope.DisplayName,Description = scope.Description,Emphasize = scope.Emphasize,Checked = check || scope.Required,Required = scope.Required};
}
  • BuildConsentViewModel
public async Task<ConsentViewModel> BuildConsentViewModelAsync(string returnUrl, ConsentInputModel model = null)
{var request = await _identityServerInteractionService.GetAuthorizationContextAsync(returnUrl);if (returnUrl == null)return null;var client = await _clientStore.FindEnabledClientByIdAsync(request.ClientId);var resources = await _resourceStore.FindEnabledResourcesByScopeAsync(request.ScopesRequested);var vm = CreateConsentViewModel(request, client, resources, model);vm.ReturnUrl = returnUrl;return vm;
}
  • ProcessConsent
public async Task<ProcessConsentResult> ProcessConsentAsync(ConsentInputModel model)
{ConsentResponse grantedConsent = null;var result = new ProcessConsentResult();if (model?.Button == "no"){grantedConsent = ConsentResponse.Denied;}else if (model?.Button == "yes"){if (model.ScopesConsented != null && model.ScopesConsented.Any()){grantedConsent = new ConsentResponse{RememberConsent = model.RememberConsent,ScopesConsented = model.ScopesConsented};}result.ValidationError = "至少选中一个权限";}if (grantedConsent != null){var request = await _identityServerInteractionService.GetAuthorizationContextAsync(model.ReturnUrl);await _identityServerInteractionService.GrantConsentAsync(request, grantedConsent);result.RedirectUrl = model.ReturnUrl;}else{var consentVideModel = await BuildConsentViewModelAsync(model.ReturnUrl, model);result.ViewModel = consentVideModel;}return result;
}

Consent页面的实现

  • Consent页面
@using CodeSnippets.IdentityCenter.Models
@model ConsentViewModel<div class="row page-header"><div class="col-sm-10"><div class="media">@if (Model.ClientLogoUrl != null){<img src="@Model.ClientLogoUrl" class="mr-3" alt="...">}<div class="media-body"><h5 class="mt-0">@Model.ClientName</h5><small>申请使用</small></div></div></div>
</div><div class="row"><div class="col-sm-8"><partial name="_ValidationSummary" /><form asp-action="Index"><input type="hidden" asp-for="ReturnUrl" />@if (Model.IdentityScopes.Any()){<div><div><span class="glyphicon glyphicon-user"></span></div>您的个人信息<ul class="list-group">@foreach (var scope in Model.IdentityScopes){<partial name="_ScopeListItem" model="@scope" />}</ul></div>}@if (Model.ResourceScopes.Any()){<div><div><span>应用访问</span></div><ul class="list-group">@foreach (var scope in Model.ResourceScopes){<partial name="_ScopeListItem" model="@scope" />}</ul></div>}@if (Model.AllowRememberConsent){<div class=""><label><input class="" asp-for="RememberConsent" /><strong>记住我的选择</strong></label></div>}<div class=""><button name="button" value="yes" class="btn btn-primary" autofocus>同意</button><button name="button" value="no" class="btn">拒绝</button></div></form></div>
</div>
  • _ScopeListItem
@using CodeSnippets.IdentityCenter.Models
@model ScopeViewModel<li class="list-group-item"><label><input class="" type="checkbox" id="scopes_@Model.Name" name="ScopesConsented" value="@Model.Name" checked="@Model.Checked" disabled="@Model.Required" /><strong>@Model.DisplayName</strong>@if (Model.Required){<input type="hidden" name="ScopesConsented" value="@Model.Name" />}@if (Model.Emphasize){<span class=""></span>}</label>@if (Model.Description != null){<div class=""><label for="scopes_@Model.Name">@Model.Description</label></div>}
</li>
  • _ValidationSummary
@if (ViewContext.ModelState.IsValid == false)
{<div class="alert alert-danger"><strong>Error</strong><div asp-validation-summary="All" class="danger"></div></div>
}

Clients修改

在此前的基础上心中Logo,描述信息等。

public static IEnumerable<Client> Clients => new List<Client>
{new Client{ClientId="client",AllowedGrantTypes=GrantTypes.ClientCredentials,ClientSecrets={new Secret("secret".Sha256())},AllowedScopes={ "CodeSnippets.WebApi" }},new Client{ClientId="mvc",ClientName="MVC测试程序",ClientUri="http://localhost:5002",LogoUri=$"http://localhost:5000/images/github.png",Description="这是一个MVC测试程序",ClientSecrets={new Secret("secret".Sha256())},AllowedGrantTypes=GrantTypes.Code,RequireConsent=true,AllowRememberConsent=true,RequirePkce=true,RedirectUris={ "http://localhost:5002/signin-oidc"},PostLogoutRedirectUris={ "http://localhost:/5002/signout-callback-oidc"},AllowedScopes=new List<string>{IdentityServerConstants.StandardScopes.OpenId,IdentityServerConstants.StandardScopes.Profile,IdentityServerConstants.StandardScopes.Email,"CodeSnippets.WebApi"   // 启用对刷新令牌的支持},AllowOfflineAccess=true}
};

IdentityServer4是开源的,文中的所有代码都可以在IdentityServer4.Quickstart.UI中找到

IdentityServer4源码

IdentityServer4
IdentityServer4.Quickstart.UI

本文源码

访问GitHub查看

几个Model

  • ConsentInputModel
public class ConsentInputModel
{public string Button { get; set; }public IEnumerable<string> ScopesConsented { get; set; }public bool RememberConsent { get; set; }public string ReturnUrl { get; set; }
}
  • ConsentViewModel
public class ConsentViewModel : ConsentInputModel
{public string ClientName { get; set; }public string ClientUrl { get; set; }public string ClientLogoUrl { get; set; }public bool AllowRememberConsent { get; set; }public IEnumerable<ScopeViewModel> IdentityScopes { get; set; }public IEnumerable<ScopeViewModel> ResourceScopes { get; set; }
}
  • ScopeViewModel
public class ScopeViewModel
{public string Name { get; set; }public string DisplayName { get; set; }public string Description { get; set; }public bool Emphasize { get; set; }public bool Required { get; set; }public bool Checked { get; set; }
}
  • ProcessConsentResult
public class ProcessConsentResult
{public string RedirectUrl { get; set; }public bool IsRedirect => RedirectUrl != null;public ConsentViewModel ViewModel { get; set; }public string ValidationError { get; set; }
}

IdentityServer4(七):Consent授权页支持相关推荐

  1. .NET Core IdentityServer4实战 第六章-Consent授权页

    在identityServer4中登陆页面只要是成功了,就会注册一个Cookie在服务器资源上,像现在大部分的网站第三方授权,都是经过一个页面,然后选需要的功能,IdentityServer4也给我们 ...

  2. 查php源码授权后门,PHP授权系统+支持盗版入库+一键黑页后门注入+卡密授权

    PHP授权系统+支持盗版入库+一键黑页后门注入+卡密授权 p stylewhite-space normal;PHP授权系统支持盗版入库一键黑页后门注入卡密授权/pp stylewhite-space ...

  3. 使用enterTextInWebElement处理qq授权页报“网络异常,请稍后再试”的解决方法

    robotium4.0之后支持处理WebElement,从此第三方的web页有更简单的解决方法. 上周五我很愉快的处理完新浪微博和腾讯微博的授权页之后,这周一处理qq的授权页,发现使用robotium ...

  4. intel六代/七代CPU不支持win7系统不包含USB3.0驱动/蓝屏/重启

    intel六代/七代CPU不支持win7系统不包含USB3.0驱动/蓝屏/重启 问题现象:原生Win7系统不包含USB3.0的驱动,所以无法使用USB3.0的U盘在USB3.0的设备上引导,且安装完系 ...

  5. 众志成城 共克时艰 TigerGraph免费开放企业级版本授权全力支持疫情防控

    新型冠状病毒肺炎疫情自发生以来,一直牵动着全国人民的心.全球领先的可扩展企业级图数据库TigerGraph宣布,利用强大的企业级图数据库产品,免费开放企业级版本授权,为政府机构.公共事业和科研机构赋能 ...

  6. Hexo文章图片存储选七牛(当然支持MD都可以)

    版权声明:本文为 Codeagles 原创文章,可以随意转载,但必须在明确位置注明出处!!! 今天一打开blog发现一个总大问题,所有文章中的图片全挂了,Hexo文章中的图片,可以放在本地,然后和静态 ...

  7. 开心网与新浪微博的app授权页的几点比较

    今天解决开心网和微薄的app授权问题,针对老版本的授权和新版本的授权,确实新的要好一点,对使用者来说更方便了,但是开心网的demo给的事例中的样式居然不兼容IE8,让人很费解.另外开心网和新浪微博的授 ...

  8. 微信第三方平台开发经验总结(四):重定向到授权页

    重定向到授权页 步骤2:引入用户进入授权页 第三方平台方可以在自己的网站:中放置"微信公众号授权"或者"小程序授权"的入口,引导公众号和小程序管理员进入授权页. ...

  9. springcloud学习(七)-Sidecar(多语言支持)

    title: springcloud学习(七)-Sidecar(多语言支持) date: 2021-1-28 tags: 微服务 springcloud学习(七)-Sidecar(多语言支持) spr ...

  10. 导弹发射各项参数计算涉及计算机应用,《计算机应用基础》复习题七(6页).doc...

    <计算机应用基础>复习题七(6页) <计算机应用基础>复习题七 一.单选题 1.第一代电子数字计算机使用的阴极射线管作为计算机的______. A:语音处理器 B:图象显示器 ...

最新文章

  1. 二次元少女生成器、会开车的神经网络...2019年最好的17个机器学习项目!
  2. 【C#】第3章学习要点(三)--常用类和结构的用法
  3. 用移位寄存器实现边沿检测(功能仿真及ISE综合)
  4. C++ Primer 5th笔记(chap 15 OOP)继承概念
  5. Java中return的用法
  6. es6 WeakMap
  7. 永远不要随便告诉别人自己的工资,工资是职场最大的陷阱
  8. 数据挖掘的方法有哪些?
  9. 怎么提高文公写作水平?公文写作报告类模板
  10. 如何屏蔽 iOS 软件自动更新,去除更新通知和标记
  11. 云计算是什么,主要具有哪些特点
  12. 如何修复dns服务器,如何修复DNS?DNS是什么?如何设置DNS?
  13. python 极客学院 正则表达式
  14. 【火炉炼AI】机器学习008-简单线性分类器解决二分类问题
  15. C语言:围圈报号排序问题
  16. 设置 XShell 的默认全局配色方案
  17. 深度学习电脑配置选择
  18. 华为,工资岗位揭秘 2010年08月02日
  19. 卷积神经网络评估模型讲解
  20. 基于51单片机的俄罗斯方块小游戏proteus仿真LCD12864原理图程序设计

热门文章

  1. 解读丨修身齐家治国平天下
  2. php主机安装教程,easypanel 主机面板安装教程
  3. pat basic 1082 射击比赛
  4. 向武 清华大学 计算机,双胞胎兄弟向威、向武同时考上清华大学
  5. Hadoop指令-周美婷
  6. 计算机编辑功能在哪,win10系统本地组策略编辑器在哪的具体步骤
  7. 将社会工程学演绎成艺术---弗兰克·威廉·阿巴内尔的传奇故事--黑客博客
  8. 在线逻辑分析仪的使用
  9. 利用一种新的灵活记分卡方法改进肽抗癌活性的预测和表征
  10. linux命令行 jdb,什么使用加多宝(jdb)在linux下调试Java程序