序言 閑來無聊,前段時間發現一個.net開源框架:masa framework。經過一些小型項目使用,發現確實挺不錯的。然後我又去閱讀了整個masa framework源碼,特此來記錄整個源碼閱讀的過程。 如有錯誤之處還請指點 MASA Framework簡介 Masa Framework是 m ...
學習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) ,我們繼續實現功能。
5.在Visual Studio 2022的解決方案資源管理器中,滑鼠左鍵選中“Utils”文件夾,右鍵單擊,在彈出菜單中選擇“添加—>新建項”,在彈出對話框中,選擇“介面”,並將介面命名為“IJWTHelper”。如下圖。並添加如下代碼:
using Microsoft.IdentityModel.Tokens;
using System.Security.Claims;
namespace BlazorAppDemo.Utils
{
public interface IJWTHelper
{
string CreateJwtToken<T>(T user);
T GetToken<T>(string Token);
IEnumerable<Claim> ParseToken(string token);
string? ValidateJwtToken(IEnumerable<Claim> jwtToken);
TokenValidationParameters ValidParameters();
}
}
6.在Visual Studio 2022的解決方案資源管理器中,滑鼠左鍵選中“Utils”文件夾,右鍵單擊,在彈出菜單中選擇“添加—>類”,在彈出對話框中,並將類命名為“JWTHelper”。並繼承IJWTHelper介面,添加如下代碼:
using BlazorAppDemo.Models;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Reflection;
using System.Security.Claims;
using System.Text;
namespace BlazorAppDemo.Utils
{
public class JWTHelper : IJWTHelper
{
private readonly IConfiguration _configuration;
private readonly JwtSecurityTokenHandler _jwtSecurityTokenHandler;
public JWTHelper(IConfiguration configuration, JwtSecurityTokenHandler jwtSecurityTokenHandler)
{
_configuration = configuration;
_jwtSecurityTokenHandler = jwtSecurityTokenHandler;
}
/// <summary>
/// 創建加密JwtToken
/// </summary>
/// <param name="user"></param>
/// <returns></returns>
public string CreateJwtToken<T>(T user)
{
var signingAlogorithm = SecurityAlgorithms.HmacSha256;
var claimList = this.CreateClaimList(user);
//Signature
//取出私鑰並以utf8編碼位元組輸出
var secretByte = Encoding.UTF8.GetBytes(_configuration["Authentication:SecretKey"]);
//使用非對稱演算法對私鑰進行加密
var signingKey = new SymmetricSecurityKey(secretByte);
//使用HmacSha256來驗證加密後的私鑰生成數字簽名
var signingCredentials = new SigningCredentials(signingKey, signingAlogorithm);
//在 RFC 7519 規範中(Section#4),一共定義了 7 個預設的 Claims。
//生成Token
var Token = new JwtSecurityToken(
issuer: _configuration["Authentication:Issuer"], //發佈者
audience: _configuration["Authentication:Audience"], //接收者
claims: claimList, //存放的用戶信息
notBefore: DateTime.UtcNow, //發佈時間
expires: DateTime.UtcNow.AddDays(1), //有效期設置為1天
signingCredentials //數字簽名
);
//生成字元串token
var TokenStr = new JwtSecurityTokenHandler().WriteToken(Token);
return TokenStr;
}
public T GetToken<T>(string Token)
{
Type t = typeof(T);
object objA = Activator.CreateInstance(t);
var b = _jwtSecurityTokenHandler.ReadJwtToken(Token);
foreach (var item in b.Claims)
{
PropertyInfo _Property = t.GetProperty(item.Type);
if (_Property != null && _Property.CanRead)
{
_Property.SetValue(objA, item.Value, null);
}
}
return (T)objA;
}
/// <summary>
/// 創建包含用戶信息的CalimList
/// </summary>
/// <param name="authUser"></param>
/// <returns></returns>
private List<Claim> CreateClaimList<T>(T authUser)
{
var Class = typeof(UserInfo);
List<Claim> claimList = new List<Claim>();
foreach (var item in Class.GetProperties())
{
if (item.Name == "Password")
{
continue;
}
claimList.Add(new Claim(ClaimTypes.Name, Convert.ToString(item.GetValue(authUser))));
}
return claimList;
}
}
}
7. 在Visual Studio 2022的解決方案資源管理器中,使用滑鼠左鍵雙擊“Auth”文件夾中的“ImitateAuthStateProvider.cs”文件,在文本編輯器中打開,對代碼進行修改。具體代碼如下:
using BlazorAppDemo.Models;
using BlazorAppDemo.Utils;
using Microsoft.AspNetCore.Components.Authorization;
using System.Security.Claims;
namespace BlazorAppDemo.Auth
{
public class ImitateAuthStateProvider : AuthenticationStateProvider
{
private readonly IJWTHelper jwt;
public ImitateAuthStateProvider(IJWTHelper _jwt) => jwt = _jwt; //DI
bool isLogin = false;
string token = string.Empty;
public override Task<AuthenticationState> GetAuthenticationStateAsync()
{
if (isLogin)
{
var claims = new List<Claim>()
{
new Claim(ClaimTypes.Name,"user"),
new Claim(ClaimTypes.Role, "admin")
};
var anonymous = new ClaimsIdentity(claims, "testAuthType");
return Task.FromResult(new AuthenticationState(new ClaimsPrincipal(anonymous)));
}
else
{
var anonymous = new ClaimsIdentity();
return Task.FromResult(new AuthenticationState(new ClaimsPrincipal(anonymous)));
}
}
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());
}
}
}
8.在Visual Studio 2022的解決方案管理器中,使用滑鼠左鍵,雙擊Program.cs文件,將之在文本編輯器中打開,將我們寫的JWTHelper與JwtSecurityTokenHandler、使用DI方式註入,添加JWT認證服務。具體代碼如下:
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;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor();
builder.Services.AddSingleton<WeatherForecastService>();
System.Console.WriteLine(ConfigHelper.Configuration["ConnectionStrings:BookContext"]);
builder.Services.AddDbContextFactory<BookContext>(opt =>
opt.UseSqlServer(ConfigHelper.Configuration["ConnectionStrings:BookContext"]));
//builder.Services.AddScoped<AuthenticationStateProvider, ImitateAuthStateProvider>();
builder.Services.AddScoped<ImitateAuthStateProvider>();
builder.Services.AddScoped<AuthenticationStateProvider>(implementationFactory =>
implementationFactory.GetRequiredService<ImitateAuthStateProvider>());
builder.Services.AddScoped<JwtSecurityTokenHandler>();
//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)
};
});
//自己寫的Jwt 擴展
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.MapBlazorHub();
app.MapFallbackToPage("/_Host");
app.UseAuthentication();
app.UseAuthorization();
app.Run();
9.在Visual Studio 2022的菜單欄上,找到“調試-->開始調試”或是按F5鍵,Visual Studio 2022會生成BlazorAppDemo應用程式,瀏覽器中會Login登錄頁面。
10.我們在用戶名輸入框中輸入用戶名,在密碼輸入框中輸入密碼,點擊“登錄”按鈕,進行模擬登錄。我們進入了系統。並且生成了JWT Token。如下圖。