學習ASP.NET Core Blazor編程系列二十八——JWT登錄(3)

来源:https://www.cnblogs.com/chillsrc/archive/2023/03/12/17207969.html
-Advertisement-
Play Games

Blazor Server,即運行在伺服器上的 Blazor 應用程式,它的優點是應用程式在首次運行時,客戶端不需要下載運行時。但它的代碼是在伺服器上執行的,然後通過 SignalR 通信來更新客戶端的 UI,所以它要求必須建立 Web Socket 連接。 用於 Blazor 應用的 Signal ...


學習ASP.NET Core Blazor編程系列文章之目錄 學習ASP.NET Core Blazor編程系列一——綜述 學習ASP.NET Core Blazor編程系列二——第一個Blazor應用程式(上)
學習ASP.NET Core Blazor編程系列三——實體 學習ASP.NET Core Blazor編程系列五——列表頁面 學習ASP.NET Core Blazor編程系列七——新增圖書 學習ASP.NET Core Blazor編程系列八——數據校驗 學習ASP.NET Core Blazor編程系列十三——路由(完) 學習ASP.NET Core Blazor編程系列十五——查詢 學習ASP.NET Core Blazor編程系列十六——排序 學習ASP.NET Core Blazor編程系列二十——文件上傳(完) 學習ASP.NET Core Blazor編程系列二十一——數據刷新  學習ASP.NET Core Blazor編程系列二十二——登錄(1) 學習ASP.NET Core Blazor編程系列二十七——JWT登錄(1)  

十二、實現登入

        在學習ASP.NET Core Blazor編程系列二十二——登錄(1)至學習ASP.NET Core Blazor編程系列二十六——登錄(5)

系列文章中學習了使用AuthenticationStateProvider實現模擬登錄。今天的文章實現JWT登錄,使用WebAPI介面來實現通過JWT令牌登錄。

  1. 在Visual Studio 2022的解決方案資源管理器中,滑鼠右鍵單擊“BlazorAppDemo”項目名稱,在彈出菜單中選擇 “添加—>新建文件夾”,並將新建文件夾改為“Api”。如下圖。

 

     2.在Visual Studio 2022的解決方案資源管理器中,滑鼠左鍵選中“Api”文件夾,右鍵單擊,在彈出菜單中選擇“添加—>新建項”,在彈出對話框中,選擇“API控制器-空”,並將控制器命名為“AuthController”。如下圖。並添加如下代碼:

 

using BlazorAppDemo.Models;
using BlazorAppDemo.Utils;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.IdentityModel.Tokens;
using Newtonsoft.Json.Linq;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
 
namespace BlazorAppDemo.Api
{
    [Route("api/[controller]")]
    [ApiController]
    public class AuthController : ControllerBase
    {
        private readonly IJWTHelper jwtHelper;
       
 
        public AuthController(IJWTHelper _IJWTHelper)
        {
            this.jwtHelper = _IJWTHelper;
           
            }
 
        [HttpPost("Login")]
            public async Task<ActionResult<UserToken>> Login(UserInfo userInfo)
        {
            //Demo用,更好的做法是查詢用戶表來實現
            if (userInfo.UserName == "admin" && userInfo.Password == "111111")
            {
                return BuildToken(userInfo);
            }
            else
            {
                UserToken userToken = new UserToken()
                {
                    StatusCode = System.Net.HttpStatusCode.Unauthorized,
                    IsSuccess = false
                   
                };
                return userToken;
            }
        }
      
 
        /// <summary>
        /// 建立Token
        /// </summary>
        /// <param name="userInfo"></param>
        /// <returns></returns>
        private UserToken BuildToken(UserInfo userInfo)
        {
          
            string jwtToken = jwtHelper.CreateJwtToken<UserInfo>(userInfo);

            //建立UserToken,回傳客戶端
            UserToken userToken = new UserToken()
            {

                StatusCode = System.Net.HttpStatusCode.OK,
                Token = jwtToken,
                ExpireTime = DateTime.Now.AddMinutes(30),
                IsSuccess= true
               
            };

            return userToken;
        }
    }
}

3.在Visual Studio 2022的解決方案資源管理器中,滑鼠左鍵選中“Models”文件夾,右鍵單擊,在彈出菜單中選擇“添加—>類”,在彈出對話框中,將類命名為“UserToken”。並添加如下代碼:

using System.Net;
namespace BlazorAppDemo.Models
{
    public class UserToken
    {

        public bool IsSuccess { get ; set; } 
        public HttpStatusCode StatusCode { get; set; }
        public string Token { get; set; }
        public DateTime ExpireTime { get; set; }
     }
}

 

4.在Visual Studio 2022的解決方案資源管理器中,滑鼠左鍵選中“Utils”文件夾,右鍵單擊,在彈出菜單中選擇“添加—>類”,在彈出對話框中,將類命名為“TokenManager”。並添加如下代碼:
using BlazorAppDemo.Models;
using System.Collections.Concurrent;
 
namespace BlazorAppDemo.Utils
{
    public class TokenManager
    {
        private const string TOKEN = "authToken";
 
        private static readonly ConcurrentDictionary<string, UserToken> tokenManager;

         static TokenManager()
        {

            tokenManager=new ConcurrentDictionary<string, UserToken>();
        }

        public static ConcurrentDictionary<string, UserToken> Instance { get { return tokenManager; } }

        public static string Token { get { return TOKEN; } }
    }
}
    5.在Visual Studio 2022的解決方案資源管理器中,滑鼠左鍵選中“Auth”文件夾,右鍵單擊,在彈出菜單中選擇“添加—>新建項”,在彈出對話框中,選擇“介面”,並將介面命名為“IAuthService”。如下圖。並添加如下代碼:
using BlazorAppDemo.Models;

namespace BlazorAppDemo.Auth
{

    public interface IAuthService
    {

        Task<UserToken> LoginAsync(UserInfo userInfo);

        Task<UserToken> LogoutAsync();
    }
}

 

6.在Visual Studio 2022的解決方案資源管理器中,滑鼠左鍵選中“Auth”文件夾,右鍵單擊,在彈出菜單中選擇“添加—>類”,在彈出對話框中,將類命名為“AuthService”。並添加如下代碼:
using BlazorAppDemo.Models;
using BlazorAppDemo.Utils;
using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.AspNetCore.Identity;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.Collections.Concurrent;
using System.Net.Http;
using System.Text;
 
namespace BlazorAppDemo.Auth
{
 
    public class AuthService : IAuthService
    {
        private readonly HttpClient httpClient;
        private readonly AuthenticationStateProvider authenticationStateProvider;
        private readonly IConfiguration configuration;
        private readonly Api.AuthController authController;
        private readonly string currentUserUrl, loginUrl, logoutUrl;
 
     
        public AuthService( HttpClient httpClient, AuthenticationStateProvider authenticationStateProvider, IConfiguration configuration,Api.AuthController authController)
        {
            this.authController = authController;
            this.httpClient = httpClient;
            this.authenticationStateProvider = authenticationStateProvider;
            this.configuration = configuration;
            currentUserUrl = configuration["AuthUrl:Current"] ?? "Auth/Current/";
            loginUrl = configuration["AuthUrl:Login"] ?? "api/Auth/Login";
            logoutUrl = configuration["AuthUrl:Logout"] ?? "/api/Auth/Logout/";
        }
        public async Task<UserToken> LoginAsync(UserInfo userInfo)
        {

            var result = authController.Login(userInfo);
            var loginResponse =  result.Result.Value;
            if (loginResponse != null && loginResponse.IsSuccess)
                {                  
                    TokenManager.Instance.TryAdd(TokenManager.Token, loginResponse);
                   ((ImitateAuthStateProvider)authenticationStateProvider).NotifyUserAuthentication(loginResponse.Token);

                    httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("bearer", loginResponse.Token);
                     return loginResponse;
                }

            return new UserToken() { IsSuccess = false };
        }

        public Task<UserToken> LogoutAsync()
        {
            throw new NotImplementedException();
        }
    }
}

 

LoginAsync登錄方法的實現功能:
  • 將賬號與密碼,發送到AuthController做驗證,驗證成功生成UserToken實例
  • 將token寫到TokenManger實例中
  • 通知前面頁面更新登錄狀態
  • 每次request的header將bearer token都帶上。

 

7. 在Visual Studio 2022的解決方案管理器中,使用滑鼠左鍵,雙擊ImitateAuthStateProvider.cs文件,對代碼進行修改。具體代碼如下:

using BlazorAppDemo.Models;
using BlazorAppDemo.Utils;
using Microsoft.AspNetCore.Components.Authorization;
using System.Net.Http;
using System.Security.Claims;
 
namespace BlazorAppDemo.Auth
{
    public class ImitateAuthStateProvider : AuthenticationStateProvider
    {
        private readonly IJWTHelper jwt;
        private AuthenticationState anonymous;
        private readonly HttpClient httpClient;
 
        public ImitateAuthStateProvider(IJWTHelper _jwt, HttpClient httpClient)
        {

            anonymous = new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity()));
            jwt = _jwt;
            this.httpClient = httpClient;
        }
 
        bool isLogin = false;
        string token = string.Empty;
        public override Task<AuthenticationState> GetAuthenticationStateAsync()
        {
            //確認是否已經登錄
            UserToken userToken;
                TokenManager.Instance.TryGetValue(TokenManager.Token,out userToken);
            string tokenInLocalStorage=string.Empty;
            if (userToken != null)
            {
                tokenInLocalStorage = userToken.Token;
            }
            if (string.IsNullOrEmpty(tokenInLocalStorage))
            {
                //沒有登錄,則返回匿名登錄者
                return Task.FromResult(anonymous);
            }
 
            //將token取出轉換為claim
            var claims = jwt.ParseToken(tokenInLocalStorage);
 
            //在每次request的header中都將加入bearer token
            httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("bearer", 
tokenInLocalStorage);
//回傳帶有user claim的AuthenticationState return Task.FromResult(new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity(claims, "jwt")))); } public void Login(UserInfo request) { //1.驗證用戶賬號密碼是否正確 if (request == null) { isLogin=false; } if (request.UserName == "user" && request.Password == "111111") { isLogin = true; token= jwt.CreateJwtToken<UserInfo>(request); Console.WriteLine($"JWT Token={token}"); } NotifyAuthenticationStateChanged(GetAuthenticationStateAsync()); } public void NotifyUserAuthentication(string token) { var claims = jwt.ParseToken(token); var authenticatedUser = new ClaimsPrincipal(new ClaimsIdentity(claims, "jwt")); var authState = Task.FromResult(new AuthenticationState(authenticatedUser)); NotifyAuthenticationStateChanged(authState); } } }

 

8. 在Visual Studio 2022的解決方案管理器中,使用滑鼠左鍵,雙擊Program.cs文件,將之在文本編輯器中打開,將我們寫的AuthController和框架中的HttpClient,使用DI方式註入,添加Controller服務。具體代碼如下:
using BlazorAppDemo.Data;
using BlazorAppDemo.Models;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.Extensions.Configuration;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Hosting;
using Microsoft.AspNetCore.Components.Authorization;
using BlazorAppDemo.Auth;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using System.Text;
using System.IdentityModel.Tokens.Jwt;
using BlazorAppDemo.Utils;
using BlazorAppDemo.Api;
 
var builder = WebApplication.CreateBuilder(args);
 
 

// Add services to the container.
builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor();
builder.Services.AddSingleton<WeatherForecastService>();
IConfiguration config = ConfigHelper.Configuration;
System.Console.WriteLine(config["ConnectionStrings:BookContext"]);
builder.Services.AddDbContextFactory<BookContext>(opt =>
   opt.UseSqlServer(ConfigHelper.Configuration["ConnectionStrings:BookContext"]));
builder.Services.AddScoped<ImitateAuthStateProvider>();
builder.Services.AddScoped<AuthenticationStateProvider>(implementationFactory =>
implementationFactory.GetRequiredService<ImitateAuthStateProvider>());
builder.Services.AddScoped<JwtSecurityTokenHandler>();
//此處的url地址改成自己實際的地址

builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri("http://localhost:7110") });
 
builder.Services.AddScoped<IAuthService, AuthService>();
builder.Services.AddScoped<AuthController>();
//JWT
//JWT認證

builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(options =>
{
    //取出私鑰
    var secretByte = Encoding.UTF8.GetBytes(builder.Configuration["Authentication:SecretKey"]);
    options.TokenValidationParameters = new TokenValidationParameters()
    {
        //驗證發佈者
        ValidateIssuer = true,
        ValidIssuer = builder.Configuration["Authentication:Issuer"],
        //驗證接收者
        ValidateAudience = true,
        ValidAudience = builder.Configuration["Authentication:Audience"],
        //驗證是否過期
        ValidateLifetime = true,
        //驗證私鑰
        IssuerSigningKey = new SymmetricSecurityKey(secretByte)
    };
});
;
builder.Services.AddScoped<IJWTHelper,JWTHelper>();
 
var app = builder.Build();
 
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
    app.UseHsts();
}
 
 
using (var scope = app.Services.CreateScope())
{
    var services = scope.ServiceProvider;
    try
    {
        Console.WriteLine("資料庫開始初始化。");
        var context = services.GetRequiredService<BookContext>();
        // requires using Microsoft.EntityFrameworkCore;
        context.Database.Migrate();
        // Requires using RazorPagesMovie.Models;
        SeedData.Initialize(services);
        Console.WriteLine("資料庫初始化結束。");
    }
 
    catch (Exception ex)
    {
        var logger = services.GetRequiredService<ILogger<Program>>();
        logger.LogError(ex, "資料庫數據初始化錯誤.");
    }
}
 

app.UseHttpsRedirection();

app.UseStaticFiles();

 
app.UseRouting();
app.MapControllers();

 
app.MapBlazorHub();
app.MapFallbackToPage("/_Host");
app.UseAuthentication();
app.UseAuthorization();

 
app.Run();

 

 

9. 在Visual Studio 2022的菜單欄上,找到“調試-->開始調試”或是按F5鍵,Visual Studio 2022會生成BlazorAppDemo應用程式,併在瀏覽器使用Rest調試插件,對api/auth/login介面進行調試,只要登入成功就可以取得token。如下圖。

 

 

10.我們在用戶名輸入框中輸入用戶名"admin",在密碼輸入框中輸入密碼"111111",點擊“登錄”按鈕,進行登錄。我們進入了系統。如下圖。

 


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

-Advertisement-
Play Games
更多相關文章
  • 零基礎 OpenGL ES 學習路線推薦 : OpenGL ES 學習目錄 >> OpenGL ES 基礎 零基礎 OpenGL ES 學習路線推薦 : OpenGL ES 學習目錄 >> OpenGL ES 特效 零基礎 OpenGL ES 學習路線推薦 : OpenGL ES 學習目錄 >> O ...
  • java 線程池預設提供了幾種拒絕策略: 這幾個策略都實現了RejectedExecutionHandler,拿DiscardOldestPolicy來說,查看源碼: 核心代碼只有2行: e.getQueue().poll() 從列表裡彈出1個(最早的)任務,以便讓隊列空出1個位置 e.execut ...
  • 什麼是Git? Git是一個版本控制系統,用於跟蹤電腦文件的變化。Git是一個跟蹤電腦文件變化的版本控制系統,用於幫助協調一個項目中幾個人的工作,同時跟蹤一段時間的進展。換句話說,我們可以說它是一個促進軟體開發中源代碼管理的工具。 Git和SVN的區別 Git是分散式版本控制系統,SVN是集中式 ...
  • 廣發基金外包面試題 說說Spring AOP 說說stream流的常用API 線程池的參數 MCVV,什麼是普通讀和快照讀 redis分片模式是什麼,redis事務,分片模式下事務會生效嗎 說說rocket mq分散式事務是怎麼做的 為什麼要劃分session,怎麼解決耦合問題 說說是怎麼解決分散式 ...
  • 最近接到一個新的需求,需要上傳2G左右的視頻文件,用測試環境的OSS試了一下,上傳需要十幾分鐘,再考慮到公司的資源問題,果斷放棄該方案。 一提到大文件上傳,我最先想到的就是各種網盤了,現在大家都喜歡將自己收藏的「小電影」上傳到網盤進行保存。網盤一般都支持斷點續傳和文件秒傳功能,減少了網路波動和網路... ...
  • 1. 垃圾回收器 1.1. 對象可以在被需要時創建,不再使用時由JVM自動回收 1.2. GC是查找不再使用的對象,然後回收這些對象相關記憶體的過程 1.2.1. 找到不使用的對象、回收其記憶體、壓縮堆記憶體 1.3. 優化垃圾回收器比跟蹤指針引起的bug要容易得多(且耗時更少) 1.4. VM必須定期搜 ...
  • 簡介 Dapper是介於Entity framework與ADO的折中選擇。既滿足手寫查詢的高性能需求,又簡化了資料庫對象映射為記憶體對象的繁雜工作。Dapper.Contrib是對Dapper的進一步封裝,使對象的基本增刪改查等操作進一步簡化。 為什麼使用Dapper.Contrib 如果僅僅使用D ...
  • 在運行期間,我們可以使用 `Emit` 來組織一段 IL 代碼,進而動態生成一個方法,甚至是一個程式集(包括類型、方法或屬性等等)。這個過程我們稱之為動態編織。這一項技術應用比較廣泛,比如數據映射(Dapper)、動態代理(AOP)等等,目的是提升大量反射而產生的性能問題。 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...