在identityServer4中登录页面只要是成功了,就会注册一个Cookie在服务器资源上,像如今大部分的网站第三方受权,都是通过一个页面,而后选须要的功能,IdentityServer4也给咱们提供了,只要你登录成功,就会跳转到Consent/Index(Get)中,因此咱们只要在其中作手脚就行了。api
在编写代码以前咱们要知道IdentityServer的三个接口, IClientStore 是存放客户端信息的, IResourceStore 是存放资源API信息的,这两个接口都是在IdentityServer4的Stores的命名空间下,还有一个接口是 IIdentityServerInteractionService 用于与IdentityServer通讯的服务,主要涉及用户交互。它能够从依赖注入系统得到,一般做为构造函数参数注入到IdentityServer的用户界面的MVC控制器中。服务器
下面咱们建立一个Consent控制器在认证服务器上,名为 ConsentController ,在其中咱们须要将这三个接口经过构造函数构造进来。mvc
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对象。async
/// <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.ide
/// <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提交,因此咱们还得弄个控制器。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.ui