.NET Core IdentityServer4實戰 第六章-Consent受權頁

  在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

 

相關文章
相關標籤/搜索