在identityServer4中登陆页面只要是成功了,就会注册一个Cookie在服务器资源上,像现在大部分的网站第三方授权,都是经过一个页面,然后选需要的功能,IdentityServer4也给我们提供了,只要你登陆成功,就会跳转到Consent/Index(Get)中,所以我们只要在其中做手脚就好了。

  在编写代码之前我们要知道IdentityServer的三个接口, IClientStore 是存放客户端信息的, IResourceStore 是存放资源API信息的,这两个接口都是在IdentityServer4的Stores的命名空间下,还有一个接口是 IIdentityServerInteractionService 用于与IdentityServer通信的服务,主要涉及用户交互。它可以从依赖注入系统获得,通常作为构造函数参数注入到IdentityServer的用户界面的MVC控制器中。

  下面我们创建一个Consent控制器在认证服务器上,名为 ConsentController ,在其中我们需要将这三个接口通过构造函数构造进来。

public class ConsentController : Controller
{
private readonly IClientStore _clientStore;
private readonly IResourceStore _resourceStore;
private readonly IIdentityServerInteractionService _identityServerInteractionService;
public ConsentController(
IClientStore clientStore,
IResourceStore resourceStore,
IIdentityServerInteractionService identityServerInteractionService)
{
_clientStore = clientStore;
_resourceStore = resourceStore;
_identityServerInteractionService = identityServerInteractionService;
}
}

在控制器中,因为登陆成功是从Account控制器调过来的,那个时候还带着ReturnUrl这个而参数,我们在这个控制器中也需要ReturnUrl,所以在Get方法中写上该参数,要不然跳转不过来的。

public async Task<IActionResult> Index(string returnUrl)
{
var model =await BuildConsentViewModel(returnUrl);return View(model);
}

其中调用了 BuildConsentViewModel 方法用于返回一个consent对象,其中我们使用 _identityServerInteractionService 接口获取了上下文,然后再通过其余的两个接口找到它客户端还有资源api的信息。然后再调用了自定义的 CreateConsentViewModel 对象创建了consent对象。

/// <summary>
/// 返回一个consent对象
/// </summary>
private async Task<ConsentVm> BuildConsentViewModel(string returlUrl)
{
//获取验证上下文
var request = await _identityServerInteractionService.GetAuthorizationContextAsync(returlUrl);
if (request == null)
return null;
//根据上下文获取client的信息以及资源Api的信息
var client = await _clientStore.FindEnabledClientByIdAsync(request.ClientId);
var resources = await _resourceStore.FindEnabledResourcesByScopeAsync(request.ScopesRequested);
//创建consent对象
var vm = CreateConsentViewModel(request,client,resources);
vm.ReturnUrl = returlUrl;
return vm;
}

在其中创建对象并返回,只不过在获取ResourceScopes的时候,它是一个ApiResource,所以需要先转换成Scopes然呢再Select一下变成我们的ViewModel.

/// <summary>
/// 创建consent对象
/// </summary>
private ConsentVm CreateConsentViewModel(AuthorizationRequest request,Client client,Resources resources)
{
var vm = new ConsentVm();
vm.ClientId = client.ClientId;
vm.Logo = client.LogoUri;
vm.ClientName = client.ClientName;
vm.ClientUrl = client.ClientUri;//客户端url
vm.RemeberConsent = client.AllowRememberConsent;//是否记住信息
vm.IdentityScopes = resources.IdentityResources.Select(i=>CreateScopeViewModel(i));
vm.ResourceScopes = resources.ApiResources.SelectMany(u => u.Scopes).Select(x => CreatesScoreViewModel(x));
return vm;
}
public ScopeVm CreatesScoreViewModel(Scope scope)
{
return new ScopeVm
{
name = scope.Name,
DisplayName = scope.DisplayName,
Description = scope.Description,
Required = scope.Required,
Checked = scope.Required,
Emphasize = scope.Emphasize
};
}
private ScopeVm CreateScopeViewModel(IdentityResource identityResource)
{
return new ScopeVm
{
name = identityResource.Name,
DisplayName = identityResource.DisplayName,
Description = identityResource.Description,
Required = identityResource.Required,
Checked = identityResource.Required,
Emphasize = identityResource.Emphasize
};
}

以上我们的控制器就完成了,现在我们搞一下视图,在视图中我们就是简单做一下,使用ConsentVm作为视图绑定对象,在之中我遇到了一个Bug,我用 @Html.Partial("_ScopeListItem", item); 的时候突然就报错了,在页面上显示一个Task一大堆的错误信息,我也不知道啥情况(望大佬解决),换成不是异步的就行了。

<p>Consent Page</p>
@using mvcWebFirstSolucation.Models;
@model ConsentVm
<div class="row page-header">
<div class="col-sm-10">
@if (!string.IsNullOrWhiteSpace(Model.Logo))
{
<div>
<img src="@Model.Logo" />
</div>
}
<h1>
@Model.ClientName
<small>欢迎来到第三方授权</small>
</h1> </div>
</div>
<div class="row">
<div class="col-sm-8">
<form asp-action="Index">
<input type="hidden" asp-for="ReturnUrl" />
<div class="panel">
<div class="panel-heading">
<span class="glyphicon glyphicon-tasks"></span>
用户信息
</div>
<ul class="list-group">
@foreach (var item in Model.IdentityScopes)
{
@Html.Partial("_ScopeListItem", item);
}
</ul>
</div>
<div class="panel">
<div class="panel-heading">
<span class="glyphicon glyphicon-tasks"></span>
应用权限
</div>
<ul class="list-group">
@foreach (var item in Model.ResourceScopes)
{
@Html.Partial("_ScopeListItem", item);
}
</ul>
</div> <div>
<label>
<input type="checkbox" asp-for="RemeberConsent" />
<strong>记住我的选择</strong>
</label>
</div>
<div>
<button value="yes" class="btn btn-primary" name="button" autofocus>同意</button>
<button value="no" name="button">取消</button>
@if (!string.IsNullOrEmpty(Model.ClientUrl))
{
<a href="@Model.ClientUrl" class="pull-right btn btn-default">
<span class="glyphicon glyphicon-info-sign"></span>
<strong>@Model.ClientUrl</strong>
</a>
}
</div>
</form>
</div>
</div>

下面是局部视图的定义,传过来的对象是 ResourceScopes 和 IdentityScopes ,但他们都是对应ScopeVm,在其中呢就是把他们哪些权限列出来,然后勾选,在它的父页面已经做了post提交,所以我们还得弄个控制器。

@using mvcWebFirstSolucation.Models;
@model ScopeVm <li>
<label>
<input type="checkbox"
name="ScopesConsented"
id="scopes_@Model.name"
value="@Model.name"
checked="@Model.Checked"
disabled="@Model.Required"/> @if (Model.Required)
{
<input type="hidden" name="ScopesConsented" value="@Model.name" />
} <strong>@Model.name</strong>
@if (Model.Emphasize)
{
<span class="glyphicon glyphicon-exclamation-sign"></span>
}
</label>
@if (!string.IsNullOrEmpty(Model.Description))
{
<div>
<label for="scopes_@Model.name">@Model.Description</label>
</div>
}
</li>

这个方法的参数是我们所自定义的实体,其中有按钮还有返回的地址,在其中我们判断了是否选择OK,选择不那就直接赋一个拒绝的指令,如果ok那么就直接判断是否有这个权力,因为我们在config中进行了配置,然后如果有,呢么就直接添加,在不==null的清空下,我们根据 returlUrl 这个字符串获取了请求信息,然后通过 GrantConsentAsync 方法直接同意了授权,然后直接跳转过去,就成功了。

        [HttpPost]
public async Task<IActionResult> Index(InputConsentViewModel viewmodel)
{
// viewmodel.ReturlUrl
ConsentResponse consentResponse = null;
if (viewmodel.Button =="no")
{
consentResponse = ConsentResponse.Denied;
}
else
{
if (viewmodel.ScopesConsented !=null && viewmodel.ScopesConsented.Any())
{
consentResponse = new ConsentResponse
{
RememberConsent = viewmodel.RemeberConsent,
ScopesConsented = viewmodel.ScopesConsented
};
}
}
if (consentResponse != null)
{
var request = await _identityServerInteractionService.GetAuthorizationContextAsync(viewmodel.ReturnUrl);
await _identityServerInteractionService.GrantConsentAsync(request, consentResponse);
return Redirect(viewmodel.ReturnUrl);
}
return View(await BuildConsentViewModel(viewmodel.ReturnUrl));
}

最后,在调试的时候一定要Client的 RequireConsent 设置为true.

.NET Core IdentityServer4实战 第六章-Consent授权页的更多相关文章

  1. .NET Core IdentityServer4实战 第三章-使用EntityFramework Core进行持久化配置

    内容:本文带大家使用IdentityServer4进行使用使用EntityFramework Core进行配置和操作数据 作者:zara(张子浩) 欢迎分享,但需在文章鲜明处留下原文地址. 前两章内容 ...

  2. .NET Core IdentityServer4实战-开篇介绍与规划

    一.开篇寄语 由于假期的无聊,我决定了一个非常有挑战性的活动,也就是在年假给大家带来一个基于OAuth 2.0的身份授权框架,它就是 IdentityServer4 ,如果没有意外的话,一定可以顺利的 ...

  3. Spring实战第六章学习笔记————渲染Web视图

    Spring实战第六章学习笔记----渲染Web视图 理解视图解析 在之前所编写的控制器方法都没有直接产生浏览器所需的HTML.这些方法只是将一些数据传入到模型中然后再将模型传递给一个用来渲染的视图. ...

  4. RxJava2实战---第六章 条件操作符和布尔操作符

    RxJava2实战---第六章 条件操作符和布尔操作符 RxJava的条件操作符主要包括以下几个: amb():给定多个Observable,只让第一个发射数据的Obsrvable发射全部数据. de ...

  5. 【无私分享:ASP.NET CORE 项目实战(第二章)】添加EF上下文对象,添加接口、实现类以及无处不在的依赖注入(DI)

    目录索引 [无私分享:ASP.NET CORE 项目实战]目录索引 简介 上一章,我们介绍了安装和新建控制器.视图,这一章我们来创建个数据模型,并且添加接口和实现类. 添加EF上下文对象 按照我们以前 ...

  6. 2017.2.28 activiti实战--第六章--任务表单(二)外置表单

    学习资料:<Activiti实战> 第六章 任务表单(二)外置表单 6.3 外置表单 考虑到动态表单的缺点(见上节),外置表单使用的更多. 外置表单的特点: 页面的原样显示 字段值的自动填 ...

  7. 2017.2.28 activiti实战--第六章--任务表单(一)动态表单

    学习资料:<Activiti实战> 第六章 任务表单(一)动态表单 内容概览:本章要完成一个OA(协同办公系统)的请假流程的设计,从实用的角度,讲解如何将activiti与业务紧密相连. ...

  8. 2017.2.22 activiti实战--第六章--任务表单

    学习资料:<Activiti实战> 第六章 任务表单 本章将一步步完成一个协同办公系统(OA)的请假流程的设计,讲解如何将Activiti和实际业务联系起来. 首先讲解动态表单与外置表单的 ...

  9. 【无私分享:ASP.NET CORE 项目实战(第九章)】创建区域Areas,添加TagHelper

    目录索引 [无私分享:ASP.NET CORE 项目实战]目录索引 简介 在Asp.net Core VS2015中,我们发现还有很多不太简便的地方,比如右击添加视图,转到试图页等功能图不见了,虽然我 ...

随机推荐

  1. DTFT、DFT、FFT

    对于一般的周期信号可以用一系列(有限个或者无穷多了)正弦波的叠加来表示.这些正弦波的频率都是某一个特定频率的倍数如5hz.2*5hz.3*5hz--(其中的 5hz 叫基频).这是傅立叶级数的思想.所 ...

  2. Cocos2d-x 3.0final 终结者系列教程09-漆节点Node中间Schedule

    怎么做HelloWorld工程HelloWorld文字实现它自己主动运动? 有的童鞋会想到使用线程.不断的变化Label的Position, 不要那样做,因为Cocos2d-x在主线程只能被改变Nod ...

  3. VS中发布并调试IIS程序(非附加进程!!!)

    筒子们,你们不觉得发布到IIS再附加进程太烦了么???看了看网上全是这种方法,这不科学!VS已经提供了更好的方式了,少年们! 流程 1.打开你的IIS,创建一个站点示例中,我创建了一个端口号为5002 ...

  4. ASP POST请求

    <!DOCTYPE html><html><head><meta http-equiv="Content-Type" content=&q ...

  5. inno setup 打包说明

     [Setup] 这个段包含用于安装程序和卸载程序的全局设置 AppId:在注册表中唯一的项名称,安装完64位系统在 HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\M ...

  6. Rxjava 学习(一)

    Rxjava是什么? RxJava是由Netflix开发的响应式扩展(Reactive Extensions)的Java实现.引用MSDN上对它的定义,Reactive Extensions是这样一个 ...

  7. WPF WindowChrome 自定义窗口

    1.wpf自定义窗口: WindowChrome类描述:https://msdn.microsoft.com/zh-cn/library/system.windows.shell.windowchro ...

  8. 【JDK源码分析】String的存储区与不可变 专题

    <Think in Java>中说:“关系操作符生成的是一个boolean结果,它们计算的是操作数的值之间的关系”. "=="判断的是两个对象的内存地址是否一样,适用于 ...

  9. Advanced Installer,搜索注册表,根据注册表选择安装路径

    原文:Advanced Installer,搜索注册表,根据注册表选择安装路径 又停了一段时间没有更新了,今天上博客,发现有位朋友就打包的时候需要搜索注册表(不同版本注册表路径不一致,需要搜索多次来确 ...

  10. WPF 用Main函数方式启动程序

    原文:WPF 用Main函数方式启动程序 WPF默认程序启动:新建project后自动生成的App.xaml中指定程序启动方式(StartupUri="MainWindow.xaml&quo ...