接上文學習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();
10.我們在使用者名稱輸入框中輸入使用者名稱,在密碼輸入框中輸入密碼,點選“登入”按鈕,進行模擬登入。我們進入了系統。並且生成了JWT Token。如下圖。