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

来源:https://www.cnblogs.com/ZaraNet/archive/2019/07/07/11146224.html
-Advertisement-
Play Games

在identityServer4中登陸頁面只要是成功了,就會註冊一個Cookie在伺服器資源上,像現在大部分的網站第三方授權,都是經過一個頁面,然後選需要的功能,IdentityServer4也給我們提供了,只要你登陸成功,就會跳轉到Consent/Index(Get)中,所以我們只要在其中做手腳就 ...


  在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="[email protected]" 
               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="[email protected]">@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.

 

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 捕捉信號 Go // 運行此程式,控制台將列印"Waiting for signal" // 按Ctrl + C 發送信號以關閉程式,將發生中斷 // 隨後控制台依次列印"Signal .."、"Exiting..." package main import ( "os" "os/signal" " ...
  • 客戶端與服務端多功能傳輸小程式 server.py ...
  • 擁抱開源的腳步,我們從來都是一直在路上;.NETCore作為後起之秀,帶給我們太多的驚喜和感動;但是也正是由於年輕,.NETCore 的生態還是不夠完善,這就非常需要我們社區的力量,需要大家一起參與,把開源社區好的工具、組件、應用接入到 .NETCore 應用中。 ...
  • 系列目錄 1. "第一章|理論基礎+實戰控制台程式實現AutoFac註入" 1. 第二章|AutoFac的常見使用套路 1. 第三章|實戰Asp.Net Framework Web程式實現AutoFac註入 1. 第四章|實戰Asp.Net Core自帶DI實現依賴註入 1. 第五章|實戰Asp.N ...
  • 非同步轉同步-PushFrame 本文通過PushFrame,實現非同步轉同步 首先有一個非同步方法,如下非同步任務延時2秒後,返回一個結果 在UI線程執行此任務,嘗試轉化為同步 PushFrame非同步轉同步的實現: 測試結果: Task不帶返回值的處理: PushFrame的缺陷 PS:pushFrame ...
  • 前言: 平時在實際工作中很少用到這個,雖然都是一些比較基礎的東西,但一旦遇到了,又不知所云。剛好最近接觸了一些相關這方面的項目,所以也算是對 這些內容重新溫習實踐了一遍。所以這篇不僅作為個人備忘,也分享給各位重溫一遍。 要學會位運算,首先要清楚什麼是位運算?程式中的所有內容在電腦記憶體中都是以二進位 ...
  • 在業務型的系統開發中,我們需要維護各種個樣的類型,比如客戶類型、客戶行業、商品類型等等,這些類型往往信息量不多,並且相似度極高,如果採用一類型一表去設計,將會造成極大的工作量,通過將這部分類型的信息進行抽象,利用欄位去存儲類型區分,共用表結構,來達到相容各種類型的功能,也就是設計一個數據字典,而對於 ...
  • 窗體設計: 代碼: private void button_src_Click(object sender, EventArgs e) { OpenFileDialog openFile = new OpenFileDialog(); openFile.Title = "請選擇待校正影像"; ope ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...