IdentityServer4(七):Consent授权页支持
Consent支持
- 添加一个Consent控制器
- 实现ConsentService
- Consent页面的实现
- Clients修改
- 附
- IdentityServer4源码
- 本文源码
- 几个Model
IdentityServer4中Consent
用于允许终端用户授予客户端对资源的访问权限,通常用于第三方客户端。我们在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授权页支持相关推荐
- .NET Core IdentityServer4实战 第六章-Consent授权页
在identityServer4中登陆页面只要是成功了,就会注册一个Cookie在服务器资源上,像现在大部分的网站第三方授权,都是经过一个页面,然后选需要的功能,IdentityServer4也给我们 ...
- 查php源码授权后门,PHP授权系统+支持盗版入库+一键黑页后门注入+卡密授权
PHP授权系统+支持盗版入库+一键黑页后门注入+卡密授权 p stylewhite-space normal;PHP授权系统支持盗版入库一键黑页后门注入卡密授权/pp stylewhite-space ...
- 使用enterTextInWebElement处理qq授权页报“网络异常,请稍后再试”的解决方法
robotium4.0之后支持处理WebElement,从此第三方的web页有更简单的解决方法. 上周五我很愉快的处理完新浪微博和腾讯微博的授权页之后,这周一处理qq的授权页,发现使用robotium ...
- intel六代/七代CPU不支持win7系统不包含USB3.0驱动/蓝屏/重启
intel六代/七代CPU不支持win7系统不包含USB3.0驱动/蓝屏/重启 问题现象:原生Win7系统不包含USB3.0的驱动,所以无法使用USB3.0的U盘在USB3.0的设备上引导,且安装完系 ...
- 众志成城 共克时艰 TigerGraph免费开放企业级版本授权全力支持疫情防控
新型冠状病毒肺炎疫情自发生以来,一直牵动着全国人民的心.全球领先的可扩展企业级图数据库TigerGraph宣布,利用强大的企业级图数据库产品,免费开放企业级版本授权,为政府机构.公共事业和科研机构赋能 ...
- Hexo文章图片存储选七牛(当然支持MD都可以)
版权声明:本文为 Codeagles 原创文章,可以随意转载,但必须在明确位置注明出处!!! 今天一打开blog发现一个总大问题,所有文章中的图片全挂了,Hexo文章中的图片,可以放在本地,然后和静态 ...
- 开心网与新浪微博的app授权页的几点比较
今天解决开心网和微薄的app授权问题,针对老版本的授权和新版本的授权,确实新的要好一点,对使用者来说更方便了,但是开心网的demo给的事例中的样式居然不兼容IE8,让人很费解.另外开心网和新浪微博的授 ...
- 微信第三方平台开发经验总结(四):重定向到授权页
重定向到授权页 步骤2:引入用户进入授权页 第三方平台方可以在自己的网站:中放置"微信公众号授权"或者"小程序授权"的入口,引导公众号和小程序管理员进入授权页. ...
- springcloud学习(七)-Sidecar(多语言支持)
title: springcloud学习(七)-Sidecar(多语言支持) date: 2021-1-28 tags: 微服务 springcloud学习(七)-Sidecar(多语言支持) spr ...
- 导弹发射各项参数计算涉及计算机应用,《计算机应用基础》复习题七(6页).doc...
<计算机应用基础>复习题七(6页) <计算机应用基础>复习题七 一.单选题 1.第一代电子数字计算机使用的阴极射线管作为计算机的______. A:语音处理器 B:图象显示器 ...
最新文章
- 二次元少女生成器、会开车的神经网络...2019年最好的17个机器学习项目!
- 【C#】第3章学习要点(三)--常用类和结构的用法
- 用移位寄存器实现边沿检测(功能仿真及ISE综合)
- C++ Primer 5th笔记(chap 15 OOP)继承概念
- Java中return的用法
- es6 WeakMap
- 永远不要随便告诉别人自己的工资,工资是职场最大的陷阱
- 数据挖掘的方法有哪些?
- 怎么提高文公写作水平?公文写作报告类模板
- 如何屏蔽 iOS 软件自动更新,去除更新通知和标记
- 云计算是什么,主要具有哪些特点
- 如何修复dns服务器,如何修复DNS?DNS是什么?如何设置DNS?
- python 极客学院 正则表达式
- 【火炉炼AI】机器学习008-简单线性分类器解决二分类问题
- C语言:围圈报号排序问题
- 设置 XShell 的默认全局配色方案
- 深度学习电脑配置选择
- 华为,工资岗位揭秘 2010年08月02日
- 卷积神经网络评估模型讲解
- 基于51单片机的俄罗斯方块小游戏proteus仿真LCD12864原理图程序设计
热门文章
- 解读丨修身齐家治国平天下
- php主机安装教程,easypanel 主机面板安装教程
- pat basic 1082 射击比赛
- 向武 清华大学 计算机,双胞胎兄弟向威、向武同时考上清华大学
- Hadoop指令-周美婷
- 计算机编辑功能在哪,win10系统本地组策略编辑器在哪的具体步骤
- 将社会工程学演绎成艺术---弗兰克·威廉·阿巴内尔的传奇故事--黑客博客
- 在线逻辑分析仪的使用
- 利用一种新的灵活记分卡方法改进肽抗癌活性的预测和表征
- linux命令行 jdb,什么使用加多宝(jdb)在linux下调试Java程序