主頁 >  其他 > 《進擊吧!Blazor!》第一章 6.安全

《進擊吧!Blazor!》第一章 6.安全

2021-02-28 10:16:36 其他

《進擊吧!Blazor!》是本人與張善友老師合作的Blazor零基礎入門系列視頻,此系列能讓一個從未接觸過Blazor的程式員掌握開發Blazor應用的能力,
視頻地址:https://space.bilibili.com/483888821/channel/detail?cid=151273
本系列文章是基于《進擊吧!Blazor!》直播內容撰寫,升級.Net5,改進問題,講解更全面,因為篇幅有限,文章中省略了部分代碼,完整示例代碼:https://github.com/TimChen44/Blazor-ToDo

作者:陳超超
Ant Design Blazor 專案貢獻者,擁有十多年從業經驗,長期基于.Net技術堆疊進行架構與開發產品的作業,現就職于正泰集團,
郵箱:timchen@live.com
歡迎各位讀者有任何問題聯系我,我們共同進步,

我的的ToDo應用基本功能已經完成,但是自己的待辦當然只有自己知道,所以我們這次給我們的應用增加一些安全方面的功能,

Blazor 身份驗證與授權

身份驗證

Blazor Server應用和 Blazor WebAssembly 應用的安全方案有所不同,

  • Blazor WebAssembly

Blazor WebAssembly 應用在客戶端上運行, 由于用戶可繞過客戶端檢查,因為用戶可修改所有客戶端代碼, 因此授權僅用于確定要顯示的 UI 選項,所有客戶端應用程式技術都是如此,

  • Blazor Server

Blazor Server應用通過使用 SignalR 創建的實時連接運行, 建立連接后,將處理基于 SignalR 的應用的身份驗證, 可基于 cookie 或一些其他持有者令牌進行身份驗證,

授權

AuthorizeView 組件根據用戶是否獲得授權來選擇性地顯示 UI 內容, 如果只需要為用戶顯示資料,而不需要在程序邏輯中使用用戶的標識,那么此方法很有用,

<AuthorizeView>
    <Authorized>
<!--驗證通過顯示-->
    </Authorized>
    <NotAuthorized>
<!--驗證不通過顯示-->
    </NotAuthorized>
</AuthorizeView>

Blazor 中使用Token

在Blazor WebAssembly模式下, 因為應用都在客戶端運行,所以使用Token作為身份認證的方式是一個比較好的選擇,
基本的使用時序圖如下

<style>#mermaid-svg-7lIxBlMiPqCvXtE6 .label{font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family);fill:#333;color:#333}#mermaid-svg-7lIxBlMiPqCvXtE6 .label text{fill:#333}#mermaid-svg-7lIxBlMiPqCvXtE6 .node rect,#mermaid-svg-7lIxBlMiPqCvXtE6 .node circle,#mermaid-svg-7lIxBlMiPqCvXtE6 .node ellipse,#mermaid-svg-7lIxBlMiPqCvXtE6 .node polygon,#mermaid-svg-7lIxBlMiPqCvXtE6 .node path{fill:#ECECFF;stroke:#9370db;stroke-width:1px}#mermaid-svg-7lIxBlMiPqCvXtE6 .node .label{text-align:center;fill:#333}#mermaid-svg-7lIxBlMiPqCvXtE6 .node.clickable{cursor:pointer}#mermaid-svg-7lIxBlMiPqCvXtE6 .arrowheadPath{fill:#333}#mermaid-svg-7lIxBlMiPqCvXtE6 .edgePath .path{stroke:#333;stroke-width:1.5px}#mermaid-svg-7lIxBlMiPqCvXtE6 .flowchart-link{stroke:#333;fill:none}#mermaid-svg-7lIxBlMiPqCvXtE6 .edgeLabel{background-color:#e8e8e8;text-align:center}#mermaid-svg-7lIxBlMiPqCvXtE6 .edgeLabel rect{opacity:0.9}#mermaid-svg-7lIxBlMiPqCvXtE6 .edgeLabel span{color:#333}#mermaid-svg-7lIxBlMiPqCvXtE6 .cluster rect{fill:#ffffde;stroke:#aa3;stroke-width:1px}#mermaid-svg-7lIxBlMiPqCvXtE6 .cluster text{fill:#333}#mermaid-svg-7lIxBlMiPqCvXtE6 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family);font-size:12px;background:#ffffde;border:1px solid #aa3;border-radius:2px;pointer-events:none;z-index:100}#mermaid-svg-7lIxBlMiPqCvXtE6 .actor{stroke:#ccf;fill:#ECECFF}#mermaid-svg-7lIxBlMiPqCvXtE6 text.actor>tspan{fill:#000;stroke:none}#mermaid-svg-7lIxBlMiPqCvXtE6 .actor-line{stroke:grey}#mermaid-svg-7lIxBlMiPqCvXtE6 .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333}#mermaid-svg-7lIxBlMiPqCvXtE6 .messageLine1{stroke-width:1.5;stroke-dasharray:2, 2;stroke:#333}#mermaid-svg-7lIxBlMiPqCvXtE6 #arrowhead path{fill:#333;stroke:#333}#mermaid-svg-7lIxBlMiPqCvXtE6 .sequenceNumber{fill:#fff}#mermaid-svg-7lIxBlMiPqCvXtE6 #sequencenumber{fill:#333}#mermaid-svg-7lIxBlMiPqCvXtE6 #crosshead path{fill:#333;stroke:#333}#mermaid-svg-7lIxBlMiPqCvXtE6 .messageText{fill:#333;stroke:#333}#mermaid-svg-7lIxBlMiPqCvXtE6 .labelBox{stroke:#ccf;fill:#ECECFF}#mermaid-svg-7lIxBlMiPqCvXtE6 .labelText,#mermaid-svg-7lIxBlMiPqCvXtE6 .labelText>tspan{fill:#000;stroke:none}#mermaid-svg-7lIxBlMiPqCvXtE6 .loopText,#mermaid-svg-7lIxBlMiPqCvXtE6 .loopText>tspan{fill:#000;stroke:none}#mermaid-svg-7lIxBlMiPqCvXtE6 .loopLine{stroke-width:2px;stroke-dasharray:2, 2;stroke:#ccf;fill:#ccf}#mermaid-svg-7lIxBlMiPqCvXtE6 .note{stroke:#aa3;fill:#fff5ad}#mermaid-svg-7lIxBlMiPqCvXtE6 .noteText,#mermaid-svg-7lIxBlMiPqCvXtE6 .noteText>tspan{fill:#000;stroke:none}#mermaid-svg-7lIxBlMiPqCvXtE6 .activation0{fill:#f4f4f4;stroke:#666}#mermaid-svg-7lIxBlMiPqCvXtE6 .activation1{fill:#f4f4f4;stroke:#666}#mermaid-svg-7lIxBlMiPqCvXtE6 .activation2{fill:#f4f4f4;stroke:#666}#mermaid-svg-7lIxBlMiPqCvXtE6 .mermaid-main-font{font-family:"trebuchet ms", verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-7lIxBlMiPqCvXtE6 .section{stroke:none;opacity:0.2}#mermaid-svg-7lIxBlMiPqCvXtE6 .section0{fill:rgba(102,102,255,0.49)}#mermaid-svg-7lIxBlMiPqCvXtE6 .section2{fill:#fff400}#mermaid-svg-7lIxBlMiPqCvXtE6 .section1,#mermaid-svg-7lIxBlMiPqCvXtE6 .section3{fill:#fff;opacity:0.2}#mermaid-svg-7lIxBlMiPqCvXtE6 .sectionTitle0{fill:#333}#mermaid-svg-7lIxBlMiPqCvXtE6 .sectionTitle1{fill:#333}#mermaid-svg-7lIxBlMiPqCvXtE6 .sectionTitle2{fill:#333}#mermaid-svg-7lIxBlMiPqCvXtE6 .sectionTitle3{fill:#333}#mermaid-svg-7lIxBlMiPqCvXtE6 .sectionTitle{text-anchor:start;font-size:11px;text-height:14px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-7lIxBlMiPqCvXtE6 .grid .tick{stroke:#d3d3d3;opacity:0.8;shape-rendering:crispEdges}#mermaid-svg-7lIxBlMiPqCvXtE6 .grid .tick text{font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-7lIxBlMiPqCvXtE6 .grid path{stroke-width:0}#mermaid-svg-7lIxBlMiPqCvXtE6 .today{fill:none;stroke:red;stroke-width:2px}#mermaid-svg-7lIxBlMiPqCvXtE6 .task{stroke-width:2}#mermaid-svg-7lIxBlMiPqCvXtE6 .taskText{text-anchor:middle;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-7lIxBlMiPqCvXtE6 .taskText:not([font-size]){font-size:11px}#mermaid-svg-7lIxBlMiPqCvXtE6 .taskTextOutsideRight{fill:#000;text-anchor:start;font-size:11px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-7lIxBlMiPqCvXtE6 .taskTextOutsideLeft{fill:#000;text-anchor:end;font-size:11px}#mermaid-svg-7lIxBlMiPqCvXtE6 .task.clickable{cursor:pointer}#mermaid-svg-7lIxBlMiPqCvXtE6 .taskText.clickable{cursor:pointer;fill:#003163 !important;font-weight:bold}#mermaid-svg-7lIxBlMiPqCvXtE6 .taskTextOutsideLeft.clickable{cursor:pointer;fill:#003163 !important;font-weight:bold}#mermaid-svg-7lIxBlMiPqCvXtE6 .taskTextOutsideRight.clickable{cursor:pointer;fill:#003163 !important;font-weight:bold}#mermaid-svg-7lIxBlMiPqCvXtE6 .taskText0,#mermaid-svg-7lIxBlMiPqCvXtE6 .taskText1,#mermaid-svg-7lIxBlMiPqCvXtE6 .taskText2,#mermaid-svg-7lIxBlMiPqCvXtE6 .taskText3{fill:#fff}#mermaid-svg-7lIxBlMiPqCvXtE6 .task0,#mermaid-svg-7lIxBlMiPqCvXtE6 .task1,#mermaid-svg-7lIxBlMiPqCvXtE6 .task2,#mermaid-svg-7lIxBlMiPqCvXtE6 .task3{fill:#8a90dd;stroke:#534fbc}#mermaid-svg-7lIxBlMiPqCvXtE6 .taskTextOutside0,#mermaid-svg-7lIxBlMiPqCvXtE6 .taskTextOutside2{fill:#000}#mermaid-svg-7lIxBlMiPqCvXtE6 .taskTextOutside1,#mermaid-svg-7lIxBlMiPqCvXtE6 .taskTextOutside3{fill:#000}#mermaid-svg-7lIxBlMiPqCvXtE6 .active0,#mermaid-svg-7lIxBlMiPqCvXtE6 .active1,#mermaid-svg-7lIxBlMiPqCvXtE6 .active2,#mermaid-svg-7lIxBlMiPqCvXtE6 .active3{fill:#bfc7ff;stroke:#534fbc}#mermaid-svg-7lIxBlMiPqCvXtE6 .activeText0,#mermaid-svg-7lIxBlMiPqCvXtE6 .activeText1,#mermaid-svg-7lIxBlMiPqCvXtE6 .activeText2,#mermaid-svg-7lIxBlMiPqCvXtE6 .activeText3{fill:#000 !important}#mermaid-svg-7lIxBlMiPqCvXtE6 .done0,#mermaid-svg-7lIxBlMiPqCvXtE6 .done1,#mermaid-svg-7lIxBlMiPqCvXtE6 .done2,#mermaid-svg-7lIxBlMiPqCvXtE6 .done3{stroke:grey;fill:#d3d3d3;stroke-width:2}#mermaid-svg-7lIxBlMiPqCvXtE6 .doneText0,#mermaid-svg-7lIxBlMiPqCvXtE6 .doneText1,#mermaid-svg-7lIxBlMiPqCvXtE6 .doneText2,#mermaid-svg-7lIxBlMiPqCvXtE6 .doneText3{fill:#000 !important}#mermaid-svg-7lIxBlMiPqCvXtE6 .crit0,#mermaid-svg-7lIxBlMiPqCvXtE6 .crit1,#mermaid-svg-7lIxBlMiPqCvXtE6 .crit2,#mermaid-svg-7lIxBlMiPqCvXtE6 .crit3{stroke:#f88;fill:red;stroke-width:2}#mermaid-svg-7lIxBlMiPqCvXtE6 .activeCrit0,#mermaid-svg-7lIxBlMiPqCvXtE6 .activeCrit1,#mermaid-svg-7lIxBlMiPqCvXtE6 .activeCrit2,#mermaid-svg-7lIxBlMiPqCvXtE6 .activeCrit3{stroke:#f88;fill:#bfc7ff;stroke-width:2}#mermaid-svg-7lIxBlMiPqCvXtE6 .doneCrit0,#mermaid-svg-7lIxBlMiPqCvXtE6 .doneCrit1,#mermaid-svg-7lIxBlMiPqCvXtE6 .doneCrit2,#mermaid-svg-7lIxBlMiPqCvXtE6 .doneCrit3{stroke:#f88;fill:#d3d3d3;stroke-width:2;cursor:pointer;shape-rendering:crispEdges}#mermaid-svg-7lIxBlMiPqCvXtE6 .milestone{transform:rotate(45deg) scale(0.8, 0.8)}#mermaid-svg-7lIxBlMiPqCvXtE6 .milestoneText{font-style:italic}#mermaid-svg-7lIxBlMiPqCvXtE6 .doneCritText0,#mermaid-svg-7lIxBlMiPqCvXtE6 .doneCritText1,#mermaid-svg-7lIxBlMiPqCvXtE6 .doneCritText2,#mermaid-svg-7lIxBlMiPqCvXtE6 .doneCritText3{fill:#000 !important}#mermaid-svg-7lIxBlMiPqCvXtE6 .activeCritText0,#mermaid-svg-7lIxBlMiPqCvXtE6 .activeCritText1,#mermaid-svg-7lIxBlMiPqCvXtE6 .activeCritText2,#mermaid-svg-7lIxBlMiPqCvXtE6 .activeCritText3{fill:#000 !important}#mermaid-svg-7lIxBlMiPqCvXtE6 .titleText{text-anchor:middle;font-size:18px;fill:#000;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-7lIxBlMiPqCvXtE6 g.classGroup text{fill:#9370db;stroke:none;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family);font-size:10px}#mermaid-svg-7lIxBlMiPqCvXtE6 g.classGroup text .title{font-weight:bolder}#mermaid-svg-7lIxBlMiPqCvXtE6 g.clickable{cursor:pointer}#mermaid-svg-7lIxBlMiPqCvXtE6 g.classGroup rect{fill:#ECECFF;stroke:#9370db}#mermaid-svg-7lIxBlMiPqCvXtE6 g.classGroup line{stroke:#9370db;stroke-width:1}#mermaid-svg-7lIxBlMiPqCvXtE6 .classLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5}#mermaid-svg-7lIxBlMiPqCvXtE6 .classLabel .label{fill:#9370db;font-size:10px}#mermaid-svg-7lIxBlMiPqCvXtE6 .relation{stroke:#9370db;stroke-width:1;fill:none}#mermaid-svg-7lIxBlMiPqCvXtE6 .dashed-line{stroke-dasharray:3}#mermaid-svg-7lIxBlMiPqCvXtE6 #compositionStart{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-7lIxBlMiPqCvXtE6 #compositionEnd{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-7lIxBlMiPqCvXtE6 #aggregationStart{fill:#ECECFF;stroke:#9370db;stroke-width:1}#mermaid-svg-7lIxBlMiPqCvXtE6 #aggregationEnd{fill:#ECECFF;stroke:#9370db;stroke-width:1}#mermaid-svg-7lIxBlMiPqCvXtE6 #dependencyStart{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-7lIxBlMiPqCvXtE6 #dependencyEnd{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-7lIxBlMiPqCvXtE6 #extensionStart{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-7lIxBlMiPqCvXtE6 #extensionEnd{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-7lIxBlMiPqCvXtE6 .commit-id,#mermaid-svg-7lIxBlMiPqCvXtE6 .commit-msg,#mermaid-svg-7lIxBlMiPqCvXtE6 .branch-label{fill:lightgrey;color:lightgrey;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-7lIxBlMiPqCvXtE6 .pieTitleText{text-anchor:middle;font-size:25px;fill:#000;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-7lIxBlMiPqCvXtE6 .slice{font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-7lIxBlMiPqCvXtE6 g.stateGroup text{fill:#9370db;stroke:none;font-size:10px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-7lIxBlMiPqCvXtE6 g.stateGroup text{fill:#9370db;fill:#333;stroke:none;font-size:10px}#mermaid-svg-7lIxBlMiPqCvXtE6 g.statediagram-cluster .cluster-label text{fill:#333}#mermaid-svg-7lIxBlMiPqCvXtE6 g.stateGroup .state-title{font-weight:bolder;fill:#000}#mermaid-svg-7lIxBlMiPqCvXtE6 g.stateGroup rect{fill:#ECECFF;stroke:#9370db}#mermaid-svg-7lIxBlMiPqCvXtE6 g.stateGroup line{stroke:#9370db;stroke-width:1}#mermaid-svg-7lIxBlMiPqCvXtE6 .transition{stroke:#9370db;stroke-width:1;fill:none}#mermaid-svg-7lIxBlMiPqCvXtE6 .stateGroup .composit{fill:white;border-bottom:1px}#mermaid-svg-7lIxBlMiPqCvXtE6 .stateGroup .alt-composit{fill:#e0e0e0;border-bottom:1px}#mermaid-svg-7lIxBlMiPqCvXtE6 .state-note{stroke:#aa3;fill:#fff5ad}#mermaid-svg-7lIxBlMiPqCvXtE6 .state-note text{fill:black;stroke:none;font-size:10px}#mermaid-svg-7lIxBlMiPqCvXtE6 .stateLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.7}#mermaid-svg-7lIxBlMiPqCvXtE6 .edgeLabel text{fill:#333}#mermaid-svg-7lIxBlMiPqCvXtE6 .stateLabel text{fill:#000;font-size:10px;font-weight:bold;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-7lIxBlMiPqCvXtE6 .node circle.state-start{fill:black;stroke:black}#mermaid-svg-7lIxBlMiPqCvXtE6 .node circle.state-end{fill:black;stroke:white;stroke-width:1.5}#mermaid-svg-7lIxBlMiPqCvXtE6 #statediagram-barbEnd{fill:#9370db}#mermaid-svg-7lIxBlMiPqCvXtE6 .statediagram-cluster rect{fill:#ECECFF;stroke:#9370db;stroke-width:1px}#mermaid-svg-7lIxBlMiPqCvXtE6 .statediagram-cluster rect.outer{rx:5px;ry:5px}#mermaid-svg-7lIxBlMiPqCvXtE6 .statediagram-state .divider{stroke:#9370db}#mermaid-svg-7lIxBlMiPqCvXtE6 .statediagram-state .title-state{rx:5px;ry:5px}#mermaid-svg-7lIxBlMiPqCvXtE6 .statediagram-cluster.statediagram-cluster .inner{fill:white}#mermaid-svg-7lIxBlMiPqCvXtE6 .statediagram-cluster.statediagram-cluster-alt .inner{fill:#e0e0e0}#mermaid-svg-7lIxBlMiPqCvXtE6 .statediagram-cluster .inner{rx:0;ry:0}#mermaid-svg-7lIxBlMiPqCvXtE6 .statediagram-state rect.basic{rx:5px;ry:5px}#mermaid-svg-7lIxBlMiPqCvXtE6 .statediagram-state rect.divider{stroke-dasharray:10,10;fill:#efefef}#mermaid-svg-7lIxBlMiPqCvXtE6 .note-edge{stroke-dasharray:5}#mermaid-svg-7lIxBlMiPqCvXtE6 .statediagram-note rect{fill:#fff5ad;stroke:#aa3;stroke-width:1px;rx:0;ry:0}:root{--mermaid-font-family: '"trebuchet ms", verdana, arial';--mermaid-font-family: "Comic Sans MS", "Comic Sans", cursive}#mermaid-svg-7lIxBlMiPqCvXtE6 .error-icon{fill:#522}#mermaid-svg-7lIxBlMiPqCvXtE6 .error-text{fill:#522;stroke:#522}#mermaid-svg-7lIxBlMiPqCvXtE6 .edge-thickness-normal{stroke-width:2px}#mermaid-svg-7lIxBlMiPqCvXtE6 .edge-thickness-thick{stroke-width:3.5px}#mermaid-svg-7lIxBlMiPqCvXtE6 .edge-pattern-solid{stroke-dasharray:0}#mermaid-svg-7lIxBlMiPqCvXtE6 .edge-pattern-dashed{stroke-dasharray:3}#mermaid-svg-7lIxBlMiPqCvXtE6 .edge-pattern-dotted{stroke-dasharray:2}#mermaid-svg-7lIxBlMiPqCvXtE6 .marker{fill:#333}#mermaid-svg-7lIxBlMiPqCvXtE6 .marker.cross{stroke:#333} :root { --mermaid-font-family: "trebuchet ms", verdana, arial;}</style> <style>#mermaid-svg-7lIxBlMiPqCvXtE6 { color: rgba(0, 0, 0, 0.75); font: ; }</style> 前端 服務端 登錄請求 驗證身份 創建Token 回傳Token 業務請求 包含Token 驗證Token 成功 前端 服務端

對于安全要求不高的應用采用這個方法簡單、易維護,完全沒有問題,

但是Token本身在安全性上存在以下兩個風險:

  1. Token無法注銷,所以可以在Token有效期內發送的非法請求,服務端無能為力,
  2. Token通過AES加密存盤在客戶端,理論上可以進行離線破解,破解后就能任意偽造Token,

因此遇到安全要求非常高的應用時,我們需要認證服務進行Token的有效性驗證

<style>#mermaid-svg-nJ5XJRPtTFEhcXNx .label{font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family);fill:#333;color:#333}#mermaid-svg-nJ5XJRPtTFEhcXNx .label text{fill:#333}#mermaid-svg-nJ5XJRPtTFEhcXNx .node rect,#mermaid-svg-nJ5XJRPtTFEhcXNx .node circle,#mermaid-svg-nJ5XJRPtTFEhcXNx .node ellipse,#mermaid-svg-nJ5XJRPtTFEhcXNx .node polygon,#mermaid-svg-nJ5XJRPtTFEhcXNx .node path{fill:#ECECFF;stroke:#9370db;stroke-width:1px}#mermaid-svg-nJ5XJRPtTFEhcXNx .node .label{text-align:center;fill:#333}#mermaid-svg-nJ5XJRPtTFEhcXNx .node.clickable{cursor:pointer}#mermaid-svg-nJ5XJRPtTFEhcXNx .arrowheadPath{fill:#333}#mermaid-svg-nJ5XJRPtTFEhcXNx .edgePath .path{stroke:#333;stroke-width:1.5px}#mermaid-svg-nJ5XJRPtTFEhcXNx .flowchart-link{stroke:#333;fill:none}#mermaid-svg-nJ5XJRPtTFEhcXNx .edgeLabel{background-color:#e8e8e8;text-align:center}#mermaid-svg-nJ5XJRPtTFEhcXNx .edgeLabel rect{opacity:0.9}#mermaid-svg-nJ5XJRPtTFEhcXNx .edgeLabel span{color:#333}#mermaid-svg-nJ5XJRPtTFEhcXNx .cluster rect{fill:#ffffde;stroke:#aa3;stroke-width:1px}#mermaid-svg-nJ5XJRPtTFEhcXNx .cluster text{fill:#333}#mermaid-svg-nJ5XJRPtTFEhcXNx div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family);font-size:12px;background:#ffffde;border:1px solid #aa3;border-radius:2px;pointer-events:none;z-index:100}#mermaid-svg-nJ5XJRPtTFEhcXNx .actor{stroke:#ccf;fill:#ECECFF}#mermaid-svg-nJ5XJRPtTFEhcXNx text.actor>tspan{fill:#000;stroke:none}#mermaid-svg-nJ5XJRPtTFEhcXNx .actor-line{stroke:grey}#mermaid-svg-nJ5XJRPtTFEhcXNx .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333}#mermaid-svg-nJ5XJRPtTFEhcXNx .messageLine1{stroke-width:1.5;stroke-dasharray:2, 2;stroke:#333}#mermaid-svg-nJ5XJRPtTFEhcXNx #arrowhead path{fill:#333;stroke:#333}#mermaid-svg-nJ5XJRPtTFEhcXNx .sequenceNumber{fill:#fff}#mermaid-svg-nJ5XJRPtTFEhcXNx #sequencenumber{fill:#333}#mermaid-svg-nJ5XJRPtTFEhcXNx #crosshead path{fill:#333;stroke:#333}#mermaid-svg-nJ5XJRPtTFEhcXNx .messageText{fill:#333;stroke:#333}#mermaid-svg-nJ5XJRPtTFEhcXNx .labelBox{stroke:#ccf;fill:#ECECFF}#mermaid-svg-nJ5XJRPtTFEhcXNx .labelText,#mermaid-svg-nJ5XJRPtTFEhcXNx .labelText>tspan{fill:#000;stroke:none}#mermaid-svg-nJ5XJRPtTFEhcXNx .loopText,#mermaid-svg-nJ5XJRPtTFEhcXNx .loopText>tspan{fill:#000;stroke:none}#mermaid-svg-nJ5XJRPtTFEhcXNx .loopLine{stroke-width:2px;stroke-dasharray:2, 2;stroke:#ccf;fill:#ccf}#mermaid-svg-nJ5XJRPtTFEhcXNx .note{stroke:#aa3;fill:#fff5ad}#mermaid-svg-nJ5XJRPtTFEhcXNx .noteText,#mermaid-svg-nJ5XJRPtTFEhcXNx .noteText>tspan{fill:#000;stroke:none}#mermaid-svg-nJ5XJRPtTFEhcXNx .activation0{fill:#f4f4f4;stroke:#666}#mermaid-svg-nJ5XJRPtTFEhcXNx .activation1{fill:#f4f4f4;stroke:#666}#mermaid-svg-nJ5XJRPtTFEhcXNx .activation2{fill:#f4f4f4;stroke:#666}#mermaid-svg-nJ5XJRPtTFEhcXNx .mermaid-main-font{font-family:"trebuchet ms", verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-nJ5XJRPtTFEhcXNx .section{stroke:none;opacity:0.2}#mermaid-svg-nJ5XJRPtTFEhcXNx .section0{fill:rgba(102,102,255,0.49)}#mermaid-svg-nJ5XJRPtTFEhcXNx .section2{fill:#fff400}#mermaid-svg-nJ5XJRPtTFEhcXNx .section1,#mermaid-svg-nJ5XJRPtTFEhcXNx .section3{fill:#fff;opacity:0.2}#mermaid-svg-nJ5XJRPtTFEhcXNx .sectionTitle0{fill:#333}#mermaid-svg-nJ5XJRPtTFEhcXNx .sectionTitle1{fill:#333}#mermaid-svg-nJ5XJRPtTFEhcXNx .sectionTitle2{fill:#333}#mermaid-svg-nJ5XJRPtTFEhcXNx .sectionTitle3{fill:#333}#mermaid-svg-nJ5XJRPtTFEhcXNx .sectionTitle{text-anchor:start;font-size:11px;text-height:14px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-nJ5XJRPtTFEhcXNx .grid .tick{stroke:#d3d3d3;opacity:0.8;shape-rendering:crispEdges}#mermaid-svg-nJ5XJRPtTFEhcXNx .grid .tick text{font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-nJ5XJRPtTFEhcXNx .grid path{stroke-width:0}#mermaid-svg-nJ5XJRPtTFEhcXNx .today{fill:none;stroke:red;stroke-width:2px}#mermaid-svg-nJ5XJRPtTFEhcXNx .task{stroke-width:2}#mermaid-svg-nJ5XJRPtTFEhcXNx .taskText{text-anchor:middle;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-nJ5XJRPtTFEhcXNx .taskText:not([font-size]){font-size:11px}#mermaid-svg-nJ5XJRPtTFEhcXNx .taskTextOutsideRight{fill:#000;text-anchor:start;font-size:11px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-nJ5XJRPtTFEhcXNx .taskTextOutsideLeft{fill:#000;text-anchor:end;font-size:11px}#mermaid-svg-nJ5XJRPtTFEhcXNx .task.clickable{cursor:pointer}#mermaid-svg-nJ5XJRPtTFEhcXNx .taskText.clickable{cursor:pointer;fill:#003163 !important;font-weight:bold}#mermaid-svg-nJ5XJRPtTFEhcXNx .taskTextOutsideLeft.clickable{cursor:pointer;fill:#003163 !important;font-weight:bold}#mermaid-svg-nJ5XJRPtTFEhcXNx .taskTextOutsideRight.clickable{cursor:pointer;fill:#003163 !important;font-weight:bold}#mermaid-svg-nJ5XJRPtTFEhcXNx .taskText0,#mermaid-svg-nJ5XJRPtTFEhcXNx .taskText1,#mermaid-svg-nJ5XJRPtTFEhcXNx .taskText2,#mermaid-svg-nJ5XJRPtTFEhcXNx .taskText3{fill:#fff}#mermaid-svg-nJ5XJRPtTFEhcXNx .task0,#mermaid-svg-nJ5XJRPtTFEhcXNx .task1,#mermaid-svg-nJ5XJRPtTFEhcXNx .task2,#mermaid-svg-nJ5XJRPtTFEhcXNx .task3{fill:#8a90dd;stroke:#534fbc}#mermaid-svg-nJ5XJRPtTFEhcXNx .taskTextOutside0,#mermaid-svg-nJ5XJRPtTFEhcXNx .taskTextOutside2{fill:#000}#mermaid-svg-nJ5XJRPtTFEhcXNx .taskTextOutside1,#mermaid-svg-nJ5XJRPtTFEhcXNx .taskTextOutside3{fill:#000}#mermaid-svg-nJ5XJRPtTFEhcXNx .active0,#mermaid-svg-nJ5XJRPtTFEhcXNx .active1,#mermaid-svg-nJ5XJRPtTFEhcXNx .active2,#mermaid-svg-nJ5XJRPtTFEhcXNx .active3{fill:#bfc7ff;stroke:#534fbc}#mermaid-svg-nJ5XJRPtTFEhcXNx .activeText0,#mermaid-svg-nJ5XJRPtTFEhcXNx .activeText1,#mermaid-svg-nJ5XJRPtTFEhcXNx .activeText2,#mermaid-svg-nJ5XJRPtTFEhcXNx .activeText3{fill:#000 !important}#mermaid-svg-nJ5XJRPtTFEhcXNx .done0,#mermaid-svg-nJ5XJRPtTFEhcXNx .done1,#mermaid-svg-nJ5XJRPtTFEhcXNx .done2,#mermaid-svg-nJ5XJRPtTFEhcXNx .done3{stroke:grey;fill:#d3d3d3;stroke-width:2}#mermaid-svg-nJ5XJRPtTFEhcXNx .doneText0,#mermaid-svg-nJ5XJRPtTFEhcXNx .doneText1,#mermaid-svg-nJ5XJRPtTFEhcXNx .doneText2,#mermaid-svg-nJ5XJRPtTFEhcXNx .doneText3{fill:#000 !important}#mermaid-svg-nJ5XJRPtTFEhcXNx .crit0,#mermaid-svg-nJ5XJRPtTFEhcXNx .crit1,#mermaid-svg-nJ5XJRPtTFEhcXNx .crit2,#mermaid-svg-nJ5XJRPtTFEhcXNx .crit3{stroke:#f88;fill:red;stroke-width:2}#mermaid-svg-nJ5XJRPtTFEhcXNx .activeCrit0,#mermaid-svg-nJ5XJRPtTFEhcXNx .activeCrit1,#mermaid-svg-nJ5XJRPtTFEhcXNx .activeCrit2,#mermaid-svg-nJ5XJRPtTFEhcXNx .activeCrit3{stroke:#f88;fill:#bfc7ff;stroke-width:2}#mermaid-svg-nJ5XJRPtTFEhcXNx .doneCrit0,#mermaid-svg-nJ5XJRPtTFEhcXNx .doneCrit1,#mermaid-svg-nJ5XJRPtTFEhcXNx .doneCrit2,#mermaid-svg-nJ5XJRPtTFEhcXNx .doneCrit3{stroke:#f88;fill:#d3d3d3;stroke-width:2;cursor:pointer;shape-rendering:crispEdges}#mermaid-svg-nJ5XJRPtTFEhcXNx .milestone{transform:rotate(45deg) scale(0.8, 0.8)}#mermaid-svg-nJ5XJRPtTFEhcXNx .milestoneText{font-style:italic}#mermaid-svg-nJ5XJRPtTFEhcXNx .doneCritText0,#mermaid-svg-nJ5XJRPtTFEhcXNx .doneCritText1,#mermaid-svg-nJ5XJRPtTFEhcXNx .doneCritText2,#mermaid-svg-nJ5XJRPtTFEhcXNx .doneCritText3{fill:#000 !important}#mermaid-svg-nJ5XJRPtTFEhcXNx .activeCritText0,#mermaid-svg-nJ5XJRPtTFEhcXNx .activeCritText1,#mermaid-svg-nJ5XJRPtTFEhcXNx .activeCritText2,#mermaid-svg-nJ5XJRPtTFEhcXNx .activeCritText3{fill:#000 !important}#mermaid-svg-nJ5XJRPtTFEhcXNx .titleText{text-anchor:middle;font-size:18px;fill:#000;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-nJ5XJRPtTFEhcXNx g.classGroup text{fill:#9370db;stroke:none;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family);font-size:10px}#mermaid-svg-nJ5XJRPtTFEhcXNx g.classGroup text .title{font-weight:bolder}#mermaid-svg-nJ5XJRPtTFEhcXNx g.clickable{cursor:pointer}#mermaid-svg-nJ5XJRPtTFEhcXNx g.classGroup rect{fill:#ECECFF;stroke:#9370db}#mermaid-svg-nJ5XJRPtTFEhcXNx g.classGroup line{stroke:#9370db;stroke-width:1}#mermaid-svg-nJ5XJRPtTFEhcXNx .classLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5}#mermaid-svg-nJ5XJRPtTFEhcXNx .classLabel .label{fill:#9370db;font-size:10px}#mermaid-svg-nJ5XJRPtTFEhcXNx .relation{stroke:#9370db;stroke-width:1;fill:none}#mermaid-svg-nJ5XJRPtTFEhcXNx .dashed-line{stroke-dasharray:3}#mermaid-svg-nJ5XJRPtTFEhcXNx #compositionStart{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-nJ5XJRPtTFEhcXNx #compositionEnd{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-nJ5XJRPtTFEhcXNx #aggregationStart{fill:#ECECFF;stroke:#9370db;stroke-width:1}#mermaid-svg-nJ5XJRPtTFEhcXNx #aggregationEnd{fill:#ECECFF;stroke:#9370db;stroke-width:1}#mermaid-svg-nJ5XJRPtTFEhcXNx #dependencyStart{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-nJ5XJRPtTFEhcXNx #dependencyEnd{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-nJ5XJRPtTFEhcXNx #extensionStart{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-nJ5XJRPtTFEhcXNx #extensionEnd{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-nJ5XJRPtTFEhcXNx .commit-id,#mermaid-svg-nJ5XJRPtTFEhcXNx .commit-msg,#mermaid-svg-nJ5XJRPtTFEhcXNx .branch-label{fill:lightgrey;color:lightgrey;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-nJ5XJRPtTFEhcXNx .pieTitleText{text-anchor:middle;font-size:25px;fill:#000;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-nJ5XJRPtTFEhcXNx .slice{font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-nJ5XJRPtTFEhcXNx g.stateGroup text{fill:#9370db;stroke:none;font-size:10px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-nJ5XJRPtTFEhcXNx g.stateGroup text{fill:#9370db;fill:#333;stroke:none;font-size:10px}#mermaid-svg-nJ5XJRPtTFEhcXNx g.statediagram-cluster .cluster-label text{fill:#333}#mermaid-svg-nJ5XJRPtTFEhcXNx g.stateGroup .state-title{font-weight:bolder;fill:#000}#mermaid-svg-nJ5XJRPtTFEhcXNx g.stateGroup rect{fill:#ECECFF;stroke:#9370db}#mermaid-svg-nJ5XJRPtTFEhcXNx g.stateGroup line{stroke:#9370db;stroke-width:1}#mermaid-svg-nJ5XJRPtTFEhcXNx .transition{stroke:#9370db;stroke-width:1;fill:none}#mermaid-svg-nJ5XJRPtTFEhcXNx .stateGroup .composit{fill:white;border-bottom:1px}#mermaid-svg-nJ5XJRPtTFEhcXNx .stateGroup .alt-composit{fill:#e0e0e0;border-bottom:1px}#mermaid-svg-nJ5XJRPtTFEhcXNx .state-note{stroke:#aa3;fill:#fff5ad}#mermaid-svg-nJ5XJRPtTFEhcXNx .state-note text{fill:black;stroke:none;font-size:10px}#mermaid-svg-nJ5XJRPtTFEhcXNx .stateLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.7}#mermaid-svg-nJ5XJRPtTFEhcXNx .edgeLabel text{fill:#333}#mermaid-svg-nJ5XJRPtTFEhcXNx .stateLabel text{fill:#000;font-size:10px;font-weight:bold;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-nJ5XJRPtTFEhcXNx .node circle.state-start{fill:black;stroke:black}#mermaid-svg-nJ5XJRPtTFEhcXNx .node circle.state-end{fill:black;stroke:white;stroke-width:1.5}#mermaid-svg-nJ5XJRPtTFEhcXNx #statediagram-barbEnd{fill:#9370db}#mermaid-svg-nJ5XJRPtTFEhcXNx .statediagram-cluster rect{fill:#ECECFF;stroke:#9370db;stroke-width:1px}#mermaid-svg-nJ5XJRPtTFEhcXNx .statediagram-cluster rect.outer{rx:5px;ry:5px}#mermaid-svg-nJ5XJRPtTFEhcXNx .statediagram-state .divider{stroke:#9370db}#mermaid-svg-nJ5XJRPtTFEhcXNx .statediagram-state .title-state{rx:5px;ry:5px}#mermaid-svg-nJ5XJRPtTFEhcXNx .statediagram-cluster.statediagram-cluster .inner{fill:white}#mermaid-svg-nJ5XJRPtTFEhcXNx .statediagram-cluster.statediagram-cluster-alt .inner{fill:#e0e0e0}#mermaid-svg-nJ5XJRPtTFEhcXNx .statediagram-cluster .inner{rx:0;ry:0}#mermaid-svg-nJ5XJRPtTFEhcXNx .statediagram-state rect.basic{rx:5px;ry:5px}#mermaid-svg-nJ5XJRPtTFEhcXNx .statediagram-state rect.divider{stroke-dasharray:10,10;fill:#efefef}#mermaid-svg-nJ5XJRPtTFEhcXNx .note-edge{stroke-dasharray:5}#mermaid-svg-nJ5XJRPtTFEhcXNx .statediagram-note rect{fill:#fff5ad;stroke:#aa3;stroke-width:1px;rx:0;ry:0}:root{--mermaid-font-family: '"trebuchet ms", verdana, arial';--mermaid-font-family: "Comic Sans MS", "Comic Sans", cursive}#mermaid-svg-nJ5XJRPtTFEhcXNx .error-icon{fill:#522}#mermaid-svg-nJ5XJRPtTFEhcXNx .error-text{fill:#522;stroke:#522}#mermaid-svg-nJ5XJRPtTFEhcXNx .edge-thickness-normal{stroke-width:2px}#mermaid-svg-nJ5XJRPtTFEhcXNx .edge-thickness-thick{stroke-width:3.5px}#mermaid-svg-nJ5XJRPtTFEhcXNx .edge-pattern-solid{stroke-dasharray:0}#mermaid-svg-nJ5XJRPtTFEhcXNx .edge-pattern-dashed{stroke-dasharray:3}#mermaid-svg-nJ5XJRPtTFEhcXNx .edge-pattern-dotted{stroke-dasharray:2}#mermaid-svg-nJ5XJRPtTFEhcXNx .marker{fill:#333}#mermaid-svg-nJ5XJRPtTFEhcXNx .marker.cross{stroke:#333} :root { --mermaid-font-family: "trebuchet ms", verdana, arial;}</style> <style>#mermaid-svg-nJ5XJRPtTFEhcXNx { color: rgba(0, 0, 0, 0.75); font: ; }</style> 前端 認證服務 服務端 登錄請求 驗證身份 創建Token 回傳Token 業務請求 包含Token 請求驗證Token 驗證Token Token有效 成功 前端 認證服務 服務端

改造ToDo

接著我們對之前的ToDo專案進行改造,讓他支持登錄功能,

ToDo.Shared

先把前后端互動所需的Dto創建了

public class LoginDto
{
    public string UserName { get; set; }
    public string Password { get; set; }
}
public class UserDto
{
    public string Name { get; set; }
    public string Token { get; set; }
}

ToDo.Server

先改造服務端,添加必要參考,撰寫身份認證代碼等

添加參考

  • Microsoft.AspNetCore.Authentication.JwtBearer

Startup.cs

添加JwtBearer配置

public void ConfigureServices(IServiceCollection services)
{
	//......
    services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
        .AddJwtBearer(options =>
        {
            options.TokenValidationParameters = new TokenValidationParameters
            {
                ValidateIssuer = true,//是否驗證Issuer
                ValidateAudience = true,//是否驗證Audience
                ValidateLifetime = true,//是否驗證失效時間
                ValidateIssuerSigningKey = true,//是否驗證SecurityKey
                ValidAudience = "guetClient",//Audience
                ValidIssuer = "guetServer",//Issuer,這兩項和簽發jwt的設定一致
                IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("123456789012345678901234567890123456789"))//拿到SecurityKey
            };
        });
}

此處定義了Token的密鑰,規則等,實際專案時可以將這些資訊放到配置中,

AuthController.cs

行政驗證控制器,用于驗證用戶身份,創建Token等,

[ApiController]
[Route("api/[controller]/[action]")]
public class AuthController : ControllerBase
{
    //登錄
    [HttpPost]
    public UserDto Login(LoginDto dto)
    {
        //模擬獲得Token
        var jwtToken = GetToken(dto.UserName);

        return new() { Name = dto.UserName, Token = jwtToken };
    }

    //獲得用戶,當頁面客戶端頁面重繪時呼叫以獲得用戶資訊
    [HttpGet]
    public UserDto GetUser()
    {
        if (User.Identity.IsAuthenticated)//如果Token有效
        {
            var name = User.Claims.First(x => x.Type == ClaimTypes.Name).Value;//從Token中拿出用戶ID
            //模擬獲得Token
            var jwtToken = GetToken(name);
            return new UserDto() { Name = name, Token = jwtToken };
        }
        else
        {
            return new UserDto() { Name = null, Token = null };
        }
    }

    public string GetToken(string name)
    {
        //此處加入賬號密碼驗證代碼

        var claims = new Claim[]
        {
            new Claim(ClaimTypes.Name,name),
            new Claim(ClaimTypes.Role,"Admin"),
        };

        var key = new SymmetricSecurityKey(System.Text.Encoding.UTF8.GetBytes("123456789012345678901234567890123456789"));
        var expires = DateTime.Now.AddDays(30);
        var token = new JwtSecurityToken(
            issuer: "guetServer",
            audience: "guetClient",
            claims: claims,
            notBefore: DateTime.Now,
            expires: expires,
            signingCredentials: new SigningCredentials(key, SecurityAlgorithms.HmacSha256));

        return new JwtSecurityTokenHandler().WriteToken(token);
    }
}

ToDo.Client

改造客戶端,讓客戶端支持身份認證

添加參考

  • Microsoft.AspNetCore.Components.Authorization

AuthenticationStateProvider

AuthenticationStateProviderAuthorizeView 組件和 CascadingAuthenticationState 組件用于獲取身份驗證狀態的基礎服務,
通常不直接使用 AuthenticationStateProvider,直接使用主要缺點是,如果基礎身份驗證狀態資料發生更改,不會自動通知組件,其次是專案中總會有一些自定義的認證邏輯,
所以我們通常寫一個類繼承他,并重寫一些我們自己的邏輯,

//AuthProvider.cs
public class AuthProvider : AuthenticationStateProvider
{
    private readonly HttpClient HttpClient;
    public string UserName { get; set; }

    public AuthProvider(HttpClient httpClient)
    {
        HttpClient = httpClient;
    }

    public async override Task<AuthenticationState> GetAuthenticationStateAsync()
    {
        //這里獲得用戶登錄狀態
        var result = await HttpClient.GetFromJsonAsync<UserDto>($"api/Auth/GetUser");

        if (result?.Name == null)
        {
            MarkUserAsLoggedOut();
            return new AuthenticationState(new ClaimsPrincipal());
        }
        else
        {
            var claims = new List<Claim>();
            claims.Add(new Claim(ClaimTypes.Name, result.Name));
            var authenticatedUser = new ClaimsPrincipal(new ClaimsIdentity(claims, "apiauth"));
            return new AuthenticationState(authenticatedUser);
        }
    }

    /// <summary>
    /// 標記授權
    /// </summary>
    /// <param name="loginModel"></param>
    /// <returns></returns>
    public void MarkUserAsAuthenticated(UserDto userDto)
    {
        HttpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("bearer", userDto.Token);
        UserName = userDto.Name;

        //此處應該根據服務器的回傳的內容進行配置本地策略,作為演示,默認添加了“Admin”
        var claims = new List<Claim>();
        claims.Add(new Claim(ClaimTypes.Name, userDto.Name));
        claims.Add(new Claim("Admin", "Admin"));

        var authenticatedUser = new ClaimsPrincipal(new ClaimsIdentity(claims, "apiauth"));
        var authState = Task.FromResult(new AuthenticationState(authenticatedUser));
        NotifyAuthenticationStateChanged(authState);

        //慈湖可以可以將Token存盤在本地存盤中,實作頁面重繪無需登錄
    }

    /// <summary>
    /// 標記注銷
    /// </summary>
    public void MarkUserAsLoggedOut()
    {
        HttpClient.DefaultRequestHeaders.Authorization = null;
        UserName = null;

        var anonymousUser = new ClaimsPrincipal(new ClaimsIdentity());
        var authState = Task.FromResult(new AuthenticationState(anonymousUser));
        NotifyAuthenticationStateChanged(authState);
    }
}

NotifyAuthenticationStateChanged方法會通知身份驗證狀態資料(例如 AuthorizeView)使用者使用新資料重新呈現,
HttpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("bearer", userDto.Token);將HTTP請求頭中加入Token,這樣之后所有的請求都會帶上Token,

Program中注入AuthProvider服務,以便于其他地方使用

//Program.cs
builder.Services.AddScoped<AuthenticationStateProvider, AuthProvider>();

Program中配置支持的策略

builder.Services.AddAuthorizationCore(option =>
{
    option.AddPolicy("Admin", policy => policy.RequireClaim("Admin"));
});

登錄界面

添加Login.razor組件,代碼如下

<div style="margin:100px">
    <Spin Spinning="isLoading">
        @if (model != null)
        {
            <Form OnFinish="OnSave" Model="@model" LabelCol="new ColLayoutParam() {Span = 6 }">
                <FormItem Label="用戶名">
                    <Input @bind-Value="context.UserName" />
                </FormItem>
                <FormItem Label="密碼">
                    <Input @bind-Value="context.Password" Type="password" />
                </FormItem>
                <FormItem  WrapperColOffset="6">
                    <Button Type="@ButtonType.Primary" HtmlType="submit">登錄</Button>
                </FormItem>
            </Form>
        }
    </Spin>
</div>
public partial class Login
{
    [Inject] public HttpClient Http { get; set; }
    [Inject] public MessageService MsgSvr { get; set; }
    [Inject] public AuthenticationStateProvider AuthProvider { get; set; }

    LoginDto model = new LoginDto();
    bool isLoading;

    async void OnLogin()
    {
        isLoading = true;

        var httpResponse = await Http.PostAsJsonAsync<LoginDto>($"api/Auth/Login", model);
        UserDto result = await httpResponse.Content.ReadFromJsonAsync<UserDto>();

        if (string.IsNullOrWhiteSpace(result?.Token) == false )
        {
            MsgSvr.Success($"登錄成功");
            ((AuthProvider)AuthProvider).MarkUserAsAuthenticated(result);
        }
        else
        {
            MsgSvr.Error($"用戶名或密碼錯誤");
        }
        isLoading = false;
       InvokeAsync( StateHasChanged);
    }
}

登錄界面代碼很簡單,就是向api/Auth/Login請求,根據回傳的結果判斷是否登入成功,
((AuthProvider)AuthProvider).MarkUserAsAuthenticated(result);標記身份認證狀態已經修改,

修改布局

修改MainLayout.razor檔案

<CascadingAuthenticationState>
    <AuthorizeView>
        <Authorized>
            <Layout>
                <Sider Style="overflow: auto;height: 100vh;position: fixed;left: 0;">
                    <div class="logo">
                        進擊吧!Blazor!
                    </div>
                    <Menu Theme="MenuTheme.Dark" Mode=@MenuMode.Inline>
                        <MenuItem RouterLink="/">
                            主頁
                        </MenuItem>
                        <MenuItem RouterLink="/today" RouterMatch="NavLinkMatch.Prefix">
                            我的一天
                        </MenuItem>
                        <MenuItem RouterLink="/star" RouterMatch="NavLinkMatch.Prefix">
                            重要任務
                        </MenuItem>
                        <MenuItem RouterLink="/search" RouterMatch="NavLinkMatch.Prefix">
                            全部
                        </MenuItem>
                    </Menu>
                </Sider>
                <Layout Class="site-layout">
                    @Body
                </Layout>
            </Layout>
        </Authorized>
        <NotAuthorized>
            <ToDo.Client.Pages.Login></ToDo.Client.Pages.Login>
        </NotAuthorized>
    </AuthorizeView>
</CascadingAuthenticationState>

當授權通過后顯示<AuthorizeView><Authorized>的選單及主頁,反之顯示<NotAuthorized>Login組件內容,
當需要根據權限顯示不同內容,可以使用<AuthorizeView>Policy屬性實作,具體是在AuthenticationStateProvider中通過配置策略,比如示例中claims.Add(new Claim("Admin", "Admin"));就添加了Admin策略,在頁面上只需<AuthorizeView Policy="Admin">就可以控制只有Admin策略的賬戶顯示其內容了,
CascadingAuthenticationState級聯身份狀態,它采用了Balzor組件中級聯機制,這樣我們可以在任意層級的組件中使用AuthorizeView來控制UI了
AuthorizeView 組件根據用戶是否獲得授權來選擇性地顯示 UI 內容,
Authorized組件中的內容只有在獲得授權時顯示,
NotAuthorized組件中的內容只有在未經授權時顯示,

修改_Imports.razor檔案,添加必要的參考

@using Microsoft.AspNetCore.Components.Authorization

運行查看效果
在這里插入圖片描述

關于更多安全

安全是一個很大的話題,這個章節只是介紹了其最簡單的實作方式,還有更多內容推薦閱讀官方檔案:https://docs.microsoft.com/zh-cn/aspnet/core/blazor/security/?view=aspnetcore-5.0

次回預告

我們通過幾張圖表,將我們ToDo應用中任務情況做個完美統計,

回傳目錄

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/264437.html

標籤:其他

上一篇:[系統安全] 二十五.WannaCry勒索病毒分析 (1)Python復現永恒之藍漏洞實作勒索加密

下一篇:Mysterious以及666兩個題的思路總結_攻防世界逆向進階區_逆向之旅012

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 網閘典型架構簡述

    網閘架構一般分為兩種:三主機的三系統架構網閘和雙主機的2+1架構網閘。 三主機架構分別為內端機、外端機和仲裁機。三機無論從軟體和硬體上均各自獨立。首先從硬體上來看,三機都用各自獨立的主板、記憶體及存盤設備。從軟體上來看,三機有各自獨立的作業系統。這樣能達到完全的三機獨立。對于“2+1”系統,“2”分為 ......

    uj5u.com 2020-09-10 02:00:44 more
  • 如何從xshell上傳檔案到centos linux虛擬機里

    如何從xshell上傳檔案到centos linux虛擬機里及:虛擬機CentOs下執行 yum -y install lrzsz命令,出現錯誤:鏡像無法找到軟體包 前言 一、安裝lrzsz步驟 二、上傳檔案 三、遇到的問題及解決方案 總結 前言 提示:其實很簡單,往虛擬機上安裝一個上傳檔案的工具 ......

    uj5u.com 2020-09-10 02:00:47 more
  • 一、SQLMAP入門

    一、SQLMAP入門 1、判斷是否存在注入 sqlmap.py -u 網址/id=1 id=1不可缺少。當注入點后面的引數大于兩個時。需要加雙引號, sqlmap.py -u "網址/id=1&uid=1" 2、判斷文本中的請求是否存在注入 從文本中加載http請求,SQLMAP可以從一個文本檔案中 ......

    uj5u.com 2020-09-10 02:00:50 more
  • Metasploit 簡單使用教程

    metasploit 簡單使用教程 浩先生, 2020-08-28 16:18:25 分類專欄: kail 網路安全 linux 文章標簽: linux資訊安全 編輯 著作權 metasploit 使用教程 前言 一、Metasploit是什么? 二、準備作業 三、具體步驟 前言 Msfconsole ......

    uj5u.com 2020-09-10 02:00:53 more
  • 游戲逆向之驅動層與用戶層通訊

    驅動層代碼: #pragma once #include <ntifs.h> #define add_code CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS) /* 更多游戲逆向視頻www.yxfzedu.com ......

    uj5u.com 2020-09-10 02:00:56 more
  • 北斗電力時鐘(北斗授時服務器)讓網路資料更精準

    北斗電力時鐘(北斗授時服務器)讓網路資料更精準 北斗電力時鐘(北斗授時服務器)讓網路資料更精準 京準電子科技官微——ahjzsz 近幾年,資訊技術的得了快速發展,互聯網在逐漸普及,其在人們生活和生產中都得到了廣泛應用,并且取得了不錯的應用效果。計算機網路資訊在電力系統中的應用,一方面使電力系統的運行 ......

    uj5u.com 2020-09-10 02:01:03 more
  • 【CTF】CTFHub 技能樹 彩蛋 writeup

    ?碎碎念 CTFHub:https://www.ctfhub.com/ 筆者入門CTF時時剛開始刷的是bugku的舊平臺,后來才有了CTFHub。 感覺不論是網頁UI設計,還是題目質量,賽事跟蹤,工具軟體都做得很不錯。 而且因為獨到的金幣制度的確讓人有一種想去刷題賺金幣的感覺。 個人還是非常喜歡這個 ......

    uj5u.com 2020-09-10 02:04:05 more
  • 02windows基礎操作

    我學到了一下幾點 Windows系統目錄結構與滲透的作用 常見Windows的服務詳解 Windows埠詳解 常用的Windows注冊表詳解 hacker DOS命令詳解(net user / type /md /rd/ dir /cd /net use copy、批處理 等) 利用dos命令制作 ......

    uj5u.com 2020-09-10 02:04:18 more
  • 03.Linux基礎操作

    我學到了以下幾點 01Linux系統介紹02系統安裝,密碼啊破解03Linux常用命令04LAMP 01LINUX windows: win03 8 12 16 19 配置不繁瑣 Linux:redhat,centos(紅帽社區版),Ubuntu server,suse unix:金融機構,證券,銀 ......

    uj5u.com 2020-09-10 02:04:30 more
  • 05HTML

    01HTML介紹 02頭部標簽講解03基礎標簽講解04表單標簽講解 HTML前段語言 js1.了解代碼2.根據代碼 懂得挖掘漏洞 (POST注入/XSS漏洞上傳)3.黑帽seo 白帽seo 客戶網站被黑帽植入劫持代碼如何處理4.熟悉html表單 <html><head><title>TDK標題,描述 ......

    uj5u.com 2020-09-10 02:04:36 more
最新发布
  • 2023年最新微信小程式抓包教程

    01 開門見山 隔一個月發一篇文章,不過分。 首先回顧一下《微信系結手機號資料庫被脫庫事件》,我也是第一時間得知了這個訊息,然后跟蹤了整件事情的經過。下面是這起事件的相關截圖以及近日流出的一萬條資料樣本: 個人認為這件事也沒什么,還不如關注一下之前45億快遞資料查詢渠道疑似在近日復活的訊息。 訊息是 ......

    uj5u.com 2023-04-20 08:48:24 more
  • web3 產品介紹:metamask 錢包 使用最多的瀏覽器插件錢包

    Metamask錢包是一種基于區塊鏈技術的數字貨幣錢包,它允許用戶在安全、便捷的環境下管理自己的加密資產。Metamask錢包是以太坊生態系統中最流行的錢包之一,它具有易于使用、安全性高和功能強大等優點。 本文將詳細介紹Metamask錢包的功能和使用方法。 一、 Metamask錢包的功能 數字資 ......

    uj5u.com 2023-04-20 08:47:46 more
  • vulnhub_Earth

    前言 靶機地址->>>vulnhub_Earth 攻擊機ip:192.168.20.121 靶機ip:192.168.20.122 參考文章 https://www.cnblogs.com/Jing-X/archive/2022/04/03/16097695.html https://www.cnb ......

    uj5u.com 2023-04-20 07:46:20 more
  • 從4k到42k,軟體測驗工程師的漲薪史,給我看哭了

    清明節一過,盲猜大家已經無心上班,在數著日子準備過五一,但一想到銀行卡里的余額……瞬間心情就不美麗了。最近,2023年高校畢業生就業調查顯示,本科畢業月平均起薪為5825元。調查一出,便有很多同學表示自己又被平均了。看著這一資料,不免讓人想到前不久中國青年報的一項調查:近六成大學生認為畢業10年內會 ......

    uj5u.com 2023-04-20 07:44:00 more
  • 最新版本 Stable Diffusion 開源 AI 繪畫工具之中文自動提詞篇

    🎈 標簽生成器 由于輸入正向提示詞 prompt 和反向提示詞 negative prompt 都是使用英文,所以對學習母語的我們非常不友好 使用網址:https://tinygeeker.github.io/p/ai-prompt-generator 這個網址是為了讓大家在使用 AI 繪畫的時候 ......

    uj5u.com 2023-04-20 07:43:36 more
  • 漫談前端自動化測驗演進之路及測驗工具分析

    隨著前端技術的不斷發展和應用程式的日益復雜,前端自動化測驗也在不斷演進。隨著 Web 應用程式變得越來越復雜,自動化測驗的需求也越來越高。如今,自動化測驗已經成為 Web 應用程式開發程序中不可或缺的一部分,它們可以幫助開發人員更快地發現和修復錯誤,提高應用程式的性能和可靠性。 ......

    uj5u.com 2023-04-20 07:43:16 more
  • CANN開發實踐:4個DVPP記憶體問題的典型案例解讀

    摘要:由于DVPP媒體資料處理功能對存放輸入、輸出資料的記憶體有更高的要求(例如,記憶體首地址128位元組對齊),因此需呼叫專用的記憶體申請介面,那么本期就分享幾個關于DVPP記憶體問題的典型案例,并給出原因分析及解決方法。 本文分享自華為云社區《FAQ_DVPP記憶體問題案例》,作者:昇騰CANN。 DVPP ......

    uj5u.com 2023-04-20 07:43:03 more
  • msf學習

    msf學習 以kali自帶的msf為例 一、msf核心模塊與功能 msf模塊都放在/usr/share/metasploit-framework/modules目錄下 1、auxiliary 輔助模塊,輔助滲透(埠掃描、登錄密碼爆破、漏洞驗證等) 2、encoders 編碼器模塊,主要包含各種編碼 ......

    uj5u.com 2023-04-20 07:42:59 more
  • Halcon軟體安裝與界面簡介

    1. 下載Halcon17版本到到本地 2. 雙擊安裝包后 3. 步驟如下 1.2 Halcon軟體安裝 界面分為四大塊 1. Halcon的五個助手 1) 影像采集助手:與相機連接,設定相機引數,采集影像 2) 標定助手:九點標定或是其它的標定,生成標定檔案及內參外參,可以將像素單位轉換為長度單位 ......

    uj5u.com 2023-04-20 07:42:17 more
  • 在MacOS下使用Unity3D開發游戲

    第一次發博客,先發一下我的游戲開發環境吧。 去年2月份買了一臺MacBookPro2021 M1pro(以下簡稱mbp),這一年來一直在用mbp開發游戲。我大致分享一下我的開發工具以及使用體驗。 1、Unity 官網鏈接: https://unity.cn/releases 我一般使用的Apple ......

    uj5u.com 2023-04-20 07:40:19 more