1. 程式人生 > >《進擊吧!Blazor!》系列入門教程 第一章 6.安全

《進擊吧!Blazor!》系列入門教程 第一章 6.安全

>《進擊吧!Blazor!》是本人與張善友老師合作的Blazor零基礎入門教程視訊,此教程能讓一個從未接觸過Blazor的程式設計師掌握開發Blazor應用的能力。 > 視訊地址:https://space.bilibili.com/483888821/channel/detail?cid=151273 > Blazor WebAssembly 是單頁應用 (SPA) 框架,用於使用 .NET 生成互動式客戶端 Web 應用,採用 C# 代替 JavaScript 來編寫前端程式碼 > 本系列文章因篇幅有限,省略了部分程式碼,完整示例程式碼:https://github.com/TimChen44/Blazor-ToDo > 作者:陳超超 > Ant Design Blazor 專案貢獻者,擁有十多年從業經驗,長期基於.Net 技術棧進行架構與開發產品的工作,現就職於正泰集團。 > 郵箱:[email protected] > 歡迎各位讀者有任何問題聯絡我,我們共同進步。 我的的 ToDo 應用基本功能已經完成,但是自己的待辦當然只有自己知道,所以我們這次給我們的應用增加一些安全方面的功能。 # Blazor 身份驗證與授權 ### 身份驗證 Blazor Server 應用和 Blazor WebAssembly 應用的安全方案有所不同。 - **Blazor WebAssembly** Blazor WebAssembly 應用在客戶端上執行。 由於使用者可繞過客戶端檢查,因為使用者可修改所有客戶端程式碼, 因此授權僅用於確定要顯示的 UI 選項,所有客戶端應用程式技術都是如此。 - **Blazor Server** Blazor Server 應用通過使用 SignalR 建立的實時連線執行。 建立連線後,將處理基於 SignalR 的應用的身份驗證。 可基於 cookie 或一些其他持有者令牌進行身份驗證。 ### 授權 `AuthorizeView` 元件根據使用者是否獲得授權來選擇性地顯示 UI 內容。 如果只需要為使用者顯示資料,而不需要在過程邏輯中使用使用者的標識,那麼此方法很有用。 ```html ``` # Blazor 中使用 Token 在 Blazor WebAssembly 模式下, 因為應用都在客戶端執行,所以使用 Token 作為身份認證的方式是一個比較好的選擇。 基本的使用時序圖如下 ```mermaid sequenceDiagram 前端 ->> 服務端: 登入請求 服務端 ->> 服務端:驗證身份 服務端 ->> 服務端:建立Token 服務端 -->> 前端: 返回Token 前端 ->> 服務端: 業務請求
包含Token 服務端 ->> 服務端:驗證Token 服務端 -->> 前端: 成功 ``` 對於安全要求不高的應用採用這個方法簡單、易維護,完全沒有問題。 但是 Token 本身在安全性上存在以下兩個風險: 1. Token 無法登出,所以可以在 Token 有效期內傳送的非法請求,服務端無能為力。 2. Token 通過 AES 加密儲存在客戶端,理論上可以進行離線破解,破解後就能任意偽造 Token。 因此遇到安全要求非常高的應用時,我們需要認證服務進行 Token 的有效性驗證 ```mermaid sequenceDiagram 前端 ->> 認證服務: 登入請求 認證服務 ->> 認證服務:驗證身份 認證服務 ->> 認證服務:建立Token 認證服務 -->> 前端: 返回Token 前端 ->> 服務端: 業務請求
包含Token 服務端 ->>認證服務: 請求驗證Token 認證服務 ->> 認證服務:驗證Token 認證服務 ->> 服務端:Token有效 服務端 -->> 前端: 成功 ``` # 改造 ToDo 接著我們對之前的 ToDo 專案進行改造,讓他支援登入功能。 ## ToDo.Shared 先把前後端互動所需的 Dto 建立了 ```csharp public class LoginDto { public string UserName { get; set; } public string Password { get; set; } } ``` ```csharp public class UserDto { public string Name { get; set; } public string Token { get; set; } } ``` ## ToDo.Server 先改造服務端,新增必要引用,編寫身份認證程式碼等 ### 新增引用 - Microsoft.AspNetCore.Authentication.JwtBearer ### Startup.cs 新增 JwtBearer 配置 ```csharp 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 等。 ```csharp [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 `AuthenticationStateProvider` 是 `AuthorizeView` 元件和 `CascadingAuthenticationState` 元件用於獲取身份驗證狀態的基礎服務。 通常不直接使用 `AuthenticationStateProvider`,直接使用主要缺點是,如果基礎身份驗證狀態資料發生更改,不會自動通知元件。其次是專案中總會有一些自定義的認證邏輯。 所以我們通常寫一個類繼承他,並重寫一些我們自己的邏輯。 ```csharp //AuthProvider.cs public class AuthProvider : AuthenticationStateProvider { private readonly HttpClient HttpClient; public string UserName { get; set; } public AuthProvider(HttpClient httpClient) { HttpClient = httpClient; } public async overr