IdentityServer4揭秘---Consent(同意页面)
授权同意页面与登录一样首先要分析页面的需要什么模型元素后建立相关的模型类
界面的话就 记住选择 、按钮、RuturnUrl、以及选择的资源Scope
- /// <summary>
- /// 主要绑定Consent界面上的一些模型
- /// </summary>
- public class ConsentViewModel
- {
- public string ReturnUrl { get; set; }
- public bool RememberConsent { get; set; }
- public string Button { get; set; }
- public IEnumerable<string> ScopesConsented { get; set; }
- }
ConsentViewModel
这里可以注意到还有Idr4的对应数据 比如客户端的一些信息,如名称、Logo、客户端的授权Scope等等、这里根据需要可以多写一些
- /// <summary>
- /// 主要绑定Idr4中关于Consent界面交互的实体字段
- /// </summary>
- public class Idr4ConsentViewModel : ConsentViewModel
- {
- public string ClientName { get; set; }
- public string ClientUrl { get; set; }
- public string ClientLogoUrl { get; set; }
- public bool AllowRememberConsent { get; set; }
- public IEnumerable<Idr4ScopeViewModel> IdentityScopes { get; set; }
- public IEnumerable<Idr4ScopeViewModel> ResouceScopes { get; set; }
- }
Idr4ConsentViewModel
这里同样需要构建Idr4Consent页面展示模型
- private async Task<Idr4ConsentViewModel> CreateIdr4ConsentViewModelAsync(string ReturnUrl)
- {
- var request = await _identityServerInteractionService.GetAuthorizationContextAsync(ReturnUrl);
- if (request != null)
- {
- //通过客户端id获取客户端信息
- var clientModel = await _clientStore.FindEnabledClientByIdAsync(request.ClientId);
- if (clientModel != null)
- {
- //获取资源Scope信息 这里包括了两种 一种是IdentityResource 和ApiResource
- var resources = await _resourceStore.FindEnabledResourcesByScopeAsync(request.ScopesRequested);
- //获取所有的权限
- // var resources = await _resourceStore.FindEnabledResourcesByScopeAsync(clientModel.AllowedScopes);
- if (resources != null && (resources.ApiResources.Any() || resources.IdentityResources.Any()))
- {
- //构造界面需要的模型
- var vm = new Idr4ConsentViewModel();
- //界面初始化时候
- vm.RememberConsent = true; //默认true
- vm.ScopesConsented = Enumerable.Empty<string>();
- vm.ReturnUrl = ReturnUrl;
- //构建关于Client的信息
- vm.ClientName = clientModel.ClientName;
- vm.ClientUrl = clientModel.ClientUri;
- vm.ClientLogoUrl = clientModel.LogoUri;
- vm.AllowRememberConsent = clientModel.AllowRememberConsent;
- vm.IdentityScopes = resources.IdentityResources.Select(x => new Idr4ScopeViewModel
- {
- Name = x.Name,
- DisplayName = x.DisplayName,
- Description = x.Description,
- Emphasize = x.Emphasize,
- Required = x.Required,
- Checked = vm.ScopesConsented.Contains(x.Name) || x.Required
- }).ToArray();
- vm.ResouceScopes = resources.ApiResources.SelectMany(x => x.Scopes).Select(k => new Idr4ScopeViewModel
- {
- Name = k.Name,
- DisplayName = k.DisplayName,
- Description = k.Description,
- Emphasize = k.Emphasize,
- Required = k.Required,
- Checked = vm.ScopesConsented.Contains(k.Name) || k.Required
- }).ToArray();
- //离线
- if (ConsentOptions.EnableOfflineAccess && resources.OfflineAccess)
- {
- vm.ResouceScopes = vm.ResouceScopes.Union(new Idr4ScopeViewModel[] {
- new Idr4ScopeViewModel{
- Name = IdentityServer4.IdentityServerConstants.StandardScopes.OfflineAccess,
- DisplayName = ConsentOptions.OfflineAccessDisplayName,
- Description = ConsentOptions.OfflineAccessDescription,
- Emphasize = true,
- Checked = vm.ScopesConsented.Contains(IdentityServer4.IdentityServerConstants.StandardScopes.OfflineAccess)
- }
- });
- }
- return vm;
- }
- else
- {
- //客户端Scope不存在 可以在界面提示并记录日志
- return null;
- }
- }
- else
- {
- //客户端不存在 可以在界面提示并记录日志
- return null;
- }
- }
- return null;
- }
CreateIdr4ConsentViewModelAsync
里面具体的话无非就是获取更具ReturnUrl地址获取用户交互接口相关的数据信息以及页面Scope绑定以及获取
值得注意的 选项required这种情况在界面上是 disabled属性 后台Action中是获取不到的,所以需要加一些影藏域
这里是Get Consent
- [HttpGet]
- public async Task<IActionResult> Consent(string ReturnUrl)
- {
- //获取请求授权信息
- var vm = await CreateIdr4ConsentViewModelAsync(ReturnUrl);
- if (vm != null)
- {
- return View(vm);
- }
- return View();
- }
Consent Get
- [HttpPost]
- [ValidateAntiForgeryToken]
- public async Task<IActionResult> Consent(Idr4ConsentViewModel model)
- {
- ConsentResponse consentResponse = null;
- if (model == null)
- {
- ModelState.AddModelError("", "数据发送异常");
- }
- //有没有选择授权
- if (model.ScopesConsented == null || model.ScopesConsented.Count() == )
- {
- ModelState.AddModelError("", "请至少选择一个权限");
- }
- //同意授权
- if (model.Button == "yes")
- {
- //选择了授权Scope
- if (model.ScopesConsented != null && model.ScopesConsented.Any())
- {
- var scopes = model.ScopesConsented;
- if (ConsentOptions.EnableOfflineAccess == false)
- {
- scopes = scopes.Where(x => x != IdentityServer4.IdentityServerConstants.StandardScopes.OfflineAccess);
- }
- consentResponse = new ConsentResponse
- {
- RememberConsent = model.RememberConsent,
- ScopesConsented = scopes
- };
- }
- }
- //不同意授权
- else if (model.Button == "no")
- {
- consentResponse = ConsentResponse.Denied;
- }
- else
- {
- var vm1 = await CreateIdr4ConsentViewModelAsync(model.ReturnUrl);
- return View(vm1);
- }
- //无论同意还是不同意都是需要跳转
- if (consentResponse != null)
- {
- var request = await _identityServerInteractionService.GetAuthorizationContextAsync(model.ReturnUrl);
- if (request == null)
- {
- ModelState.AddModelError("", "客户端登录验证不匹配");
- }
- //if (consentResponse == ConsentResponse.Denied)
- //{
- // string url = new Uri(request.RedirectUri).Authority;
- // return Redirect(url);
- //}
- //沟通Idr4服务端实现授权
- await _identityServerInteractionService.GrantConsentAsync(request, consentResponse);
- return Redirect(model.ReturnUrl);
- }
- var vm = await CreateIdr4ConsentViewModelAsync(model.ReturnUrl);
- if (vm != null)
- {
- return View(vm);
- }
- return View();
- }
Consent Post
- @using SSOServer.Models;
- @model Idr4ConsentViewModel
- @{
- Layout = null;
- }
- <!DOCTYPE html>
- <html>
- <head>
- <meta name="viewport" content="width=device-width" />
- <title>确认授权页面</title>
- </head>
- <body>
- <div>
- <div><img src="@Model.ClientLogoUrl" width="" height="" /></div>
- <div>@Model.ClientName</div>
- <div><a href="@Model.ClientUrl" target="_blank"> @Model.ClientUrl</a></div>
- </div>
- <div>
- <div asp-validation-summary="All"></div>
- <form asp-action="Consent" class="consent-form">
- <input type="hidden" asp-for="ReturnUrl" />
- <div>请求你的授权</div>
- @if (Model.IdentityScopes.Any())
- {
- <div class="panel panel-default consent-buttons">
- <div class="panel-heading">
- <span class="glyphicon glyphicon-user"></span>
- 个人信息
- </div>
- <ul class="list-group">
- @foreach (var scope in Model.IdentityScopes)
- {
- <li class="list-group-item">
- <label>
- <input class="consent-scopecheck"
- type="checkbox"
- name="ScopesConsented"
- id="scopes_@scope.Name"
- value="@scope.Name"
- checked="@scope.Checked"
- disabled="@scope.Required" />
- @if (scope.Required)
- {
- <input type="hidden"
- name="ScopesConsented"
- value="@scope.Name" />
- }
- <strong>@scope.DisplayName</strong>
- @if (scope.Emphasize)
- {
- <span class="glyphicon glyphicon-exclamation-sign"></span>
- }
- </label>
- @if (scope.Required)
- {
- <span><em>(必需)</em></span>
- }
- @if (scope.Description != null)
- {
- <div class="consent-description">
- <label for="scopes_@scope.Name">@scope.Description</label>
- </div>
- }
- </li>
- }
- </ul>
- </div>
- }
- @if (Model.ResouceScopes.Any())
- {
- <div class="panel panel-default">
- <div class="panel-heading">
- <span class="glyphicon glyphicon-tasks"></span>
- 应用授权
- </div>
- <ul class="list-group">
- @foreach (var scope in Model.ResouceScopes)
- {
- <li class="list-group-item">
- <label>
- <input class="consent-scopecheck"
- type="checkbox"
- name="ScopesConsented"
- id="scopes_@scope.Name"
- value="@scope.Name"
- checked="@scope.Checked"
- disabled="@scope.Required" />
- @if (scope.Required)
- {
- <input type="hidden"
- name="ScopesConsented"
- value="@scope.Name" />
- }
- <strong>@scope.DisplayName</strong>
- @if (scope.Emphasize)
- {
- <span class="glyphicon glyphicon-exclamation-sign"></span>
- }
- </label>
- @if (scope.Required)
- {
- <span><em>(必需)</em></span>
- }
- @if (scope.Description != null)
- {
- <div class="consent-description">
- <label for="scopes_@scope.Name">@scope.Description</label>
- </div>
- }
- </li>
- }
- </ul>
- </div>
- }
- @if (Model.AllowRememberConsent)
- {
- <div class="consent-remember">
- <label>
- <input class="consent-scopecheck" asp-for="RememberConsent" />
- <strong>记住选择</strong>
- </label>
- </div>
- }
- <div class="consent-buttons">
- <button name="button" value="yes" class="btn btn-primary" autofocus>是, 允许</button>
- <button name="button" value="no" class="btn">否,不允许</button>
- </div>
- </form>
- </div>
- </body>
- </html>
Consent View