翼度科技»论坛 编程开发 .net 查看内容

学习ASP.NET Core Blazor编程系列二十八——JWT登录(3)

6

主题

6

帖子

18

积分

新手上路

Rank: 1

积分
18
学习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) 十二、实现登入


系列文章中学习了使用AuthenticationStateProvider实现模拟登录。今天的文章实现JWT登录,使用WebAPI接口来实现通过JWT令牌登录。


  • 在Visual Studio 2022的解决方案资源管理器中,鼠标右键单击“BlazorAppDemo”项目名称,在弹出菜单中选择 “添加—>新建文件夹”,并将新建文件夹改为“Api”。如下图。
 

     2.在Visual Studio 2022的解决方案资源管理器中,鼠标左键选中“Api”文件夹,右键单击,在弹出菜单中选择“添加—>新建项”,在弹出对话框中,选择“API控制器-空”,并将控制器命名为“AuthController”。如下图。并添加如下代码:

 
  1. using BlazorAppDemo.Models;
  2. using BlazorAppDemo.Utils;
  3. using Microsoft.AspNetCore.Http;
  4. using Microsoft.AspNetCore.Identity;
  5. using Microsoft.AspNetCore.Mvc;
  6. using Microsoft.Extensions.Configuration;
  7. using Microsoft.IdentityModel.Tokens;
  8. using Newtonsoft.Json.Linq;
  9. using System.IdentityModel.Tokens.Jwt;
  10. using System.Security.Claims;
  11. using System.Text;
  12. namespace BlazorAppDemo.Api
  13. {
  14.     [Route("api/[controller]")]
  15.     [ApiController]
  16.     public class AuthController : ControllerBase
  17.     {
  18.         private readonly IJWTHelper jwtHelper;
  19.       
  20.         public AuthController(IJWTHelper _IJWTHelper)
  21.         {
  22.             this.jwtHelper = _IJWTHelper;
  23.            
  24.             }
  25.         [HttpPost("Login")]
  26.             public async Task<ActionResult<UserToken>> Login(UserInfo userInfo)
  27.         {
  28.             //Demo用,更好的做法是查询用户表来实现
  29.             if (userInfo.UserName == "admin" && userInfo.Password == "111111")
  30.             {
  31.                 return BuildToken(userInfo);
  32.             }
  33.             else
  34.             {
  35.                 UserToken userToken = new UserToken()
  36.                 {
  37.                     StatusCode = System.Net.HttpStatusCode.Unauthorized,
  38.                     IsSuccess = false
  39.                   
  40.                 };
  41.                 return userToken;
  42.             }
  43.         }
  44.       
  45.         /// <summary>
  46.         /// 建立Token
  47.         /// </summary>
  48.         /// <param name="userInfo"></param>
  49.         /// <returns></returns>
  50.         private UserToken BuildToken(UserInfo userInfo)
  51.         {
  52.          
  53.             string jwtToken = jwtHelper.CreateJwtToken<UserInfo>(userInfo);
  54.             //建立UserToken,回传客户端
  55.             UserToken userToken = new UserToken()
  56.             {
  57.                 StatusCode = System.Net.HttpStatusCode.OK,
  58.                 Token = jwtToken,
  59.                 ExpireTime = DateTime.Now.AddMinutes(30),
  60.                 IsSuccess= true
  61.                
  62.             };
  63.             return userToken;
  64.         }
  65.     }
  66. }
复制代码
3.在Visual Studio 2022的解决方案资源管理器中,鼠标左键选中“Models”文件夹,右键单击,在弹出菜单中选择“添加—>类”,在弹出对话框中,将类命名为“UserToken”。并添加如下代码:
  1. using System.Net;
  2. namespace BlazorAppDemo.Models
  3. {
  4.     public class UserToken
  5.     {
  6.         public bool IsSuccess { get ; set; }
  7.         public HttpStatusCode StatusCode { get; set; }
  8.         public string Token { get; set; }
  9.         public DateTime ExpireTime { get; set; }
  10.      }
  11. }
复制代码
 
4.在Visual Studio 2022的解决方案资源管理器中,鼠标左键选中“Utils”文件夹,右键单击,在弹出菜单中选择“添加—>类”,在弹出对话框中,将类命名为“TokenManager”。并添加如下代码:
  1. using BlazorAppDemo.Models;
  2. using System.Collections.Concurrent;
  3. namespace BlazorAppDemo.Utils
  4. {
  5.     public class TokenManager
  6.     {
  7.         private const string TOKEN = "authToken";
  8.         private static readonly ConcurrentDictionary<string, UserToken> tokenManager;
  9.          static TokenManager()
  10.         {
  11.             tokenManager=new ConcurrentDictionary<string, UserToken>();
  12.         }
  13.         public static ConcurrentDictionary<string, UserToken> Instance { get { return tokenManager; } }
  14.         public static string Token { get { return TOKEN; } }
  15.     }
  16. }
复制代码
    5.在Visual Studio 2022的解决方案资源管理器中,鼠标左键选中“Auth”文件夹,右键单击,在弹出菜单中选择“添加—>新建项”,在弹出对话框中,选择“接口”,并将接口命名为“IAuthService”。如下图。并添加如下代码:
  1. using BlazorAppDemo.Models;
  2. namespace BlazorAppDemo.Auth
  3. {
  4.     public interface IAuthService
  5.     {
  6.         Task<UserToken> LoginAsync(UserInfo userInfo);
  7.         Task<UserToken> LogoutAsync();
  8.     }
  9. }
复制代码
 
6.在Visual Studio 2022的解决方案资源管理器中,鼠标左键选中“Auth”文件夹,右键单击,在弹出菜单中选择“添加—>类”,在弹出对话框中,将类命名为“AuthService”。并添加如下代码:
  1. using BlazorAppDemo.Models;
  2. using BlazorAppDemo.Utils;
  3. using Microsoft.AspNetCore.Components.Authorization;
  4. using Microsoft.AspNetCore.Identity;
  5. using Newtonsoft.Json;
  6. using Newtonsoft.Json.Linq;
  7. using System.Collections.Concurrent;
  8. using System.Net.Http;
  9. using System.Text;
  10. namespace BlazorAppDemo.Auth
  11. {
  12.     public class AuthService : IAuthService
  13.     {
  14.         private readonly HttpClient httpClient;
  15.         private readonly AuthenticationStateProvider authenticationStateProvider;
  16.         private readonly IConfiguration configuration;
  17.         private readonly Api.AuthController authController;
  18.         private readonly string currentUserUrl, loginUrl, logoutUrl;
  19.      
  20.         public AuthService( HttpClient httpClient, AuthenticationStateProvider authenticationStateProvider, IConfiguration configuration,Api.AuthController authController)
  21.         {
  22.             this.authController = authController;
  23.             this.httpClient = httpClient;
  24.             this.authenticationStateProvider = authenticationStateProvider;
  25.             this.configuration = configuration;
  26.             currentUserUrl = configuration["AuthUrl:Current"] ?? "Auth/Current/";
  27.             loginUrl = configuration["AuthUrl:Login"] ?? "api/Auth/Login";
  28.             logoutUrl = configuration["AuthUrl:Logout"] ?? "/api/Auth/Logout/";
  29.         }
  30.         public async Task<UserToken> LoginAsync(UserInfo userInfo)
  31.         {
  32.             var result = authController.Login(userInfo);
  33.             var loginResponse =  result.Result.Value;
  34.             if (loginResponse != null && loginResponse.IsSuccess)
  35.                 {                  
  36.                     TokenManager.Instance.TryAdd(TokenManager.Token, loginResponse);
  37.                    ((ImitateAuthStateProvider)authenticationStateProvider).NotifyUserAuthentication(loginResponse.Token);
  38.                     httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("bearer", loginResponse.Token);
  39.                      return loginResponse;
  40.                 }
  41.             return new UserToken() { IsSuccess = false };
  42.         }
  43.         public Task<UserToken> LogoutAsync()
  44.         {
  45.             throw new NotImplementedException();
  46.         }
  47.     }
  48. }
复制代码
 
LoginAsync登录方法的实现功能:

  • 將账号与密码,发送到AuthController做验证,验证成功生成UserToken实例
  • 将token写到TokenManger实例中
  • 通知前面页面更新登录状态
  • 每次request的header将bearer token都带上。
 

7. 在Visual Studio 2022的解决方案管理器中,使用鼠标左键,双击ImitateAuthStateProvider.cs文件,对代码进行修改。具体代码如下:
  1. using BlazorAppDemo.Models;
  2. using BlazorAppDemo.Utils;
  3. using Microsoft.AspNetCore.Components.Authorization;
  4. using System.Net.Http;
  5. using System.Security.Claims;
  6. namespace BlazorAppDemo.Auth
  7. {
  8.     public class ImitateAuthStateProvider : AuthenticationStateProvider
  9.     {
  10.         private readonly IJWTHelper jwt;
  11.         private AuthenticationState anonymous;
  12.         private readonly HttpClient httpClient;
  13.         public ImitateAuthStateProvider(IJWTHelper _jwt, HttpClient httpClient)
  14.         {
  15.             anonymous = new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity()));
  16.             jwt = _jwt;
  17.             this.httpClient = httpClient;
  18.         }
  19.         bool isLogin = false;
  20.         string token = string.Empty;
  21.         public override Task<AuthenticationState> GetAuthenticationStateAsync()
  22.         {
  23.             //确认是否已经登录
  24.             UserToken userToken;
  25.                 TokenManager.Instance.TryGetValue(TokenManager.Token,out userToken);
  26.             string tokenInLocalStorage=string.Empty;
  27.             if (userToken != null)
  28.             {
  29.                 tokenInLocalStorage = userToken.Token;
  30.             }
  31.             if (string.IsNullOrEmpty(tokenInLocalStorage))
  32.             {
  33.                 //沒有登录,则返回匿名登录者
  34.                 return Task.FromResult(anonymous);
  35.             }
  36.             //將token取出转换为claim
  37.             var claims = jwt.ParseToken(tokenInLocalStorage);
  38.             //在每次request的header中都将加入bearer token
  39.             httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("bearer", <br>tokenInLocalStorage);
  40.             //回传带有user claim的AuthenticationState
  41.             return Task.FromResult(new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity(claims, "jwt"))));
  42.         }
  43.         public void Login(UserInfo request)
  44.         {
  45.             //1.验证用户账号密码是否正确
  46.             if (request == null)
  47.             {
  48.                 isLogin=false;
  49.             }
  50.             if (request.UserName == "user" && request.Password == "111111")
  51.             {
  52.                 isLogin = true;
  53.                token= jwt.CreateJwtToken<UserInfo>(request);
  54.                 Console.WriteLine($"JWT Token={token}");
  55.             }
  56.             NotifyAuthenticationStateChanged(GetAuthenticationStateAsync());
  57.         }
  58.         public void NotifyUserAuthentication(string token)
  59.         {
  60.             var claims = jwt.ParseToken(token);
  61.             var authenticatedUser = new ClaimsPrincipal(new ClaimsIdentity(claims, "jwt"));
  62.             var authState = Task.FromResult(new AuthenticationState(authenticatedUser));
  63.             NotifyAuthenticationStateChanged(authState);
  64.        }
  65.     }
  66. }
复制代码
 
8. 在Visual Studio 2022的解决方案管理器中,使用鼠标左键,双击Program.cs文件,将之在文本编辑器中打开,将我们写的AuthController和框架中的HttpClient,使用DI方式注入,添加Controller服务。具体代码如下:
  1. using BlazorAppDemo.Data;
  2. using BlazorAppDemo.Models;
  3. using Microsoft.AspNetCore.Components;
  4. using Microsoft.AspNetCore.Components.Web;
  5. using Microsoft.Extensions.Configuration;
  6. using Microsoft.EntityFrameworkCore;
  7. using Microsoft.Extensions.Hosting;
  8. using Microsoft.AspNetCore.Components.Authorization;
  9. using BlazorAppDemo.Auth;
  10. using Microsoft.AspNetCore.Authentication.JwtBearer;
  11. using Microsoft.IdentityModel.Tokens;
  12. using System.Text;
  13. using System.IdentityModel.Tokens.Jwt;
  14. using BlazorAppDemo.Utils;
  15. using BlazorAppDemo.Api;
  16. var builder = WebApplication.CreateBuilder(args);
  17. // Add services to the container.
  18. builder.Services.AddRazorPages();
  19. builder.Services.AddServerSideBlazor();
  20. builder.Services.AddSingleton<WeatherForecastService>();
  21. IConfiguration config = ConfigHelper.Configuration;
  22. System.Console.WriteLine(config["ConnectionStrings:BookContext"]);
  23. builder.Services.AddDbContextFactory<BookContext>(opt =>
  24.    opt.UseSqlServer(ConfigHelper.Configuration["ConnectionStrings:BookContext"]));
  25. builder.Services.AddScoped<ImitateAuthStateProvider>();
  26. builder.Services.AddScoped<AuthenticationStateProvider>(implementationFactory =>
  27. implementationFactory.GetRequiredService<ImitateAuthStateProvider>());
  28. builder.Services.AddScoped<JwtSecurityTokenHandler>();
  29. //此处的url地址改成自己实际的地址
  30. builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri("http://localhost:7110") });
  31. builder.Services.AddScoped<IAuthService, AuthService>();
  32. builder.Services.AddScoped<AuthController>();
  33. //JWT
  34. //JWT认证
  35. builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(options =>
  36. {
  37.     //取出私钥
  38.     var secretByte = Encoding.UTF8.GetBytes(builder.Configuration["Authentication:SecretKey"]);
  39.     options.TokenValidationParameters = new TokenValidationParameters()
  40.     {
  41.         //验证发布者
  42.         ValidateIssuer = true,
  43.         ValidIssuer = builder.Configuration["Authentication:Issuer"],
  44.         //验证接收者
  45.         ValidateAudience = true,
  46.         ValidAudience = builder.Configuration["Authentication:Audience"],
  47.         //验证是否过期
  48.         ValidateLifetime = true,
  49.         //验证私钥
  50.         IssuerSigningKey = new SymmetricSecurityKey(secretByte)
  51.     };
  52. });
  53. ;
  54. builder.Services.AddScoped<IJWTHelper,JWTHelper>();
  55. var app = builder.Build();
  56. // Configure the HTTP request pipeline.
  57. if (!app.Environment.IsDevelopment())
  58. {
  59.     app.UseExceptionHandler("/Error");
  60.     // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
  61.     app.UseHsts();
  62. }
  63. using (var scope = app.Services.CreateScope())
  64. {
  65.     var services = scope.ServiceProvider;
  66.     try
  67.     {
  68.         Console.WriteLine("数据库开始初始化。");
  69.         var context = services.GetRequiredService<BookContext>();
  70.         // requires using Microsoft.EntityFrameworkCore;
  71.         context.Database.Migrate();
  72.         // Requires using RazorPagesMovie.Models;
  73.         SeedData.Initialize(services);
  74.         Console.WriteLine("数据库初始化结束。");
  75.     }
  76.     catch (Exception ex)
  77.     {
  78.         var logger = services.GetRequiredService<ILogger<Program>>();
  79.         logger.LogError(ex, "数据库数据初始化错误.");
  80.     }
  81. }
  82. app.UseHttpsRedirection();
  83. app.UseStaticFiles();
  84. app.UseRouting();
  85. app.MapControllers();
  86. app.MapBlazorHub();
  87. app.MapFallbackToPage("/_Host");
  88. app.UseAuthentication();
  89. app.UseAuthorization();
  90. app.Run();
复制代码
 
9. 在Visual Studio 2022的菜单栏上,找到“调试-->开始调试”或是按F5键,Visual Studio 2022会生成BlazorAppDemo应用程序,并在浏览器使用Rest调试插件,对api/auth/login接口进行调试,只要登入成功就可以取得token。如下图。
 

 
10.我们在用户名输入框中输入用户名"admin",在密码输入框中输入密码"111111",点击“登录”按钮,进行登录。我们进入了系统。如下图。


 

来源:https://www.cnblogs.com/chillsrc/archive/2023/03/12/17207969.html
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x

举报 回复 使用道具