From 4fa451be971407b5922f5bc63fb7adbe19777e01 Mon Sep 17 00:00:00 2001 From: xiang <27800734@qq.com> Date: Mon, 12 May 2025 23:57:05 +0800 Subject: [PATCH] 1 --- .../Admins/Auths/UserLoginCommandHandler.cs | 23 +++++-- .../Admins/Auths/ValidateCommandHandler.cs | 62 +++++++++++++++++++ .../GetConfigurationQueryHandler.cs | 1 + Yes.Application/GlobalUsings.cs | 6 +- .../Areas/Admin/Pages/Article/Add.cshtml.cs | 2 +- .../Admin/Pages/Article/Detail.cshtml.cs | 2 +- .../Areas/Admin/Pages/Article/Index.cshtml.cs | 2 +- Yes.Blog/Areas/Admin/Pages/BasePageModel.cs | 11 ++++ .../Areas/Admin/Pages/Category/Add.cshtml.cs | 2 +- .../Admin/Pages/Category/Detail.cshtml.cs | 2 +- .../Admin/Pages/Category/Index.cshtml.cs | 2 +- .../Areas/Admin/Pages/Comment/Index.cshtml.cs | 2 +- .../Admin/Pages/Configuration/Index.cshtml | 10 +++ .../Admin/Pages/Configuration/Index.cshtml.cs | 2 +- Yes.Blog/Areas/Admin/Pages/Error.cshtml.cs | 2 +- Yes.Blog/Areas/Admin/Pages/Index.cshtml.cs | 2 +- Yes.Blog/Areas/Admin/Pages/Login.cshtml.cs | 2 +- Yes.Blog/Areas/Admin/Pages/Page/Add.cshtml.cs | 2 +- .../Areas/Admin/Pages/Page/Detail.cshtml.cs | 2 +- .../Areas/Admin/Pages/Page/Index.cshtml.cs | 2 +- .../Areas/Admin/Pages/Tag/Detail.cshtml.cs | 2 +- .../Areas/Admin/Pages/Tag/Index.cshtml.cs | 2 +- .../Areas/Admin/Pages/Theme/Change.cshtml.cs | 2 +- .../Areas/Admin/Pages/Theme/Detail.cshtml.cs | 2 +- .../Areas/Admin/Pages/Theme/Import.cshtml.cs | 2 +- .../Areas/Admin/Pages/Theme/Index.cshtml.cs | 2 +- Yes.Blog/Areas/Admin/Pages/User/Add.cshtml.cs | 2 +- .../Areas/Admin/Pages/User/Detail.cshtml.cs | 2 +- .../Areas/Admin/Pages/User/Index.cshtml.cs | 2 +- .../Areas/Admin/Pages/User/Password.cshtml.cs | 2 +- .../Areas/Admin/Pages/User/Profile.cshtml.cs | 2 +- Yes.Blog/Areas/Install/Pages/Index.cshtml.cs | 4 +- Yes.Blog/DependencyInjection.cs | 24 +++++-- .../Endpoints/Admins/AdminEndpointScheme.cs | 10 --- .../Admins/Auths/UserLoginEndpoint.cs | 8 ++- .../Admins/Auths/UserLogoutEndpoint.cs | 2 +- .../Admins/Auths/ValidateEndpoint.cs | 34 ++++++++++ .../Endpoints/Blogs/CreateCommentEndpoint.cs | 2 +- Yes.Blog/Endpoints/Blogs/IndexEndpoint.cs | 2 +- Yes.Blog/Endpoints/Blogs/InstallEndpoint.cs | 2 +- Yes.Blog/Endpoints/Blogs/SearchEndpoint.cs | 2 +- Yes.Blog/Endpoints/Blogs/VersionEndpoint.cs | 2 +- Yes.Blog/GlobalUsings.cs | 6 +- Yes.Blog/appsettings.json | 18 ++++-- Yes.Blog/wwwroot/admin/js/login.js | 12 ++++ Yes.Blog/wwwroot/admin/js/main.js | 2 +- Yes.Domain/Configurations/BlogSettings.cs | 6 ++ .../Core/Exceptions/InvalidTokenException.cs | 11 ++++ .../AuthenticationExtensions.cs | 43 ++++++++----- .../Authorizations/AuthenticationScheme.cs | 7 ++- .../Authorizations/JwtProvider.cs | 12 ++-- .../User/UserApiAuthorizeAttribute.cs | 6 +- .../User/UserAuthorizeAttribute.cs | 12 ---- .../User/UserPageAuthorizeAttribute.cs | 12 ++++ Yes.Infrastructure/GlobalUsings.cs | 5 +- Yes.Infrastructure/Helpers/JwtHelper.cs | 42 +++++++++++++ Yes.Infrastructure/Yes.Infrastructure.csproj | 1 + 57 files changed, 344 insertions(+), 106 deletions(-) create mode 100644 Yes.Application/Admins/Auths/ValidateCommandHandler.cs create mode 100644 Yes.Blog/Areas/Admin/Pages/BasePageModel.cs delete mode 100644 Yes.Blog/Endpoints/Admins/AdminEndpointScheme.cs create mode 100644 Yes.Blog/Endpoints/Admins/Auths/ValidateEndpoint.cs create mode 100644 Yes.Domain/Core/Exceptions/InvalidTokenException.cs delete mode 100644 Yes.Infrastructure/Authorizations/User/UserAuthorizeAttribute.cs create mode 100644 Yes.Infrastructure/Authorizations/User/UserPageAuthorizeAttribute.cs create mode 100644 Yes.Infrastructure/Helpers/JwtHelper.cs diff --git a/Yes.Application/Admins/Auths/UserLoginCommandHandler.cs b/Yes.Application/Admins/Auths/UserLoginCommandHandler.cs index 287a073..f7ffdb3 100644 --- a/Yes.Application/Admins/Auths/UserLoginCommandHandler.cs +++ b/Yes.Application/Admins/Auths/UserLoginCommandHandler.cs @@ -2,12 +2,12 @@ { public record UserLoginCommand(string Name, string Password) : IRequest; - public record UserLoginCommandResponse(string Token, IdentityInfo Identity); + public record UserLoginCommandResponse(string Token, IdentityInfo Identity, int TokenLifetimeMinutes); - public class UserLoginCommandHandler(BlogDbContext db) : IRequestHandler + public class UserLoginCommandHandler(BlogDbContext db, IOptionsMonitor options) : IRequestHandler { private readonly BlogDbContext _db = db; - + private readonly BlogSettings _settings = options.CurrentValue; public async Task Handle(UserLoginCommand request, CancellationToken cancellationToken) { var user = _db.Users.FirstOrDefault(x => x.Name == request.Name); @@ -18,12 +18,23 @@ user.CheckPassword(request.Password); - return new UserLoginCommandResponse("", new IdentityInfo + var role = user.IsSystem ? IdentityRoleEnum.Admin : IdentityRoleEnum.User; + var claims = new[] + { + new Claim(ClaimTypes.Name, user.Name), + new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()), + new Claim("Role", role.ToString()), + new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()) + }; + + var token = JwtHelper.GenerateToken(claims, _settings.SecretKey, _settings.TokenLifetimeMinutes); + + return new UserLoginCommandResponse(token, new IdentityInfo { Name = user.Name, Id = user.Id, - Role = user.IsSystem ? IdentityRoleEnum.Admin : IdentityRoleEnum.User - }); + Role = role + }, _settings.TokenLifetimeMinutes); } } diff --git a/Yes.Application/Admins/Auths/ValidateCommandHandler.cs b/Yes.Application/Admins/Auths/ValidateCommandHandler.cs new file mode 100644 index 0000000..2bf6eb8 --- /dev/null +++ b/Yes.Application/Admins/Auths/ValidateCommandHandler.cs @@ -0,0 +1,62 @@ + + +namespace Yes.Application.Admins.Auths +{ + public record ValidateCommand(string Token) : IRequest; + + public record ValidateCommandResponse(string Token, IdentityInfo Identity, int TokenLifetimeMinutes); + + public class ValidateCommandHandler(BlogDbContext db, IOptionsMonitor options) : IRequestHandler + { + private readonly BlogDbContext _db = db; + private readonly BlogSettings _settings = options.CurrentValue; + public async Task Handle(ValidateCommand request, CancellationToken cancellationToken) + { + var token = request.Token; + if (string.IsNullOrEmpty(token)) + { + throw new InvalidTokenException(); + } + + try + { + var securityKeyBase64 = JwtHelper.GenerateKey(_settings.SecretKey ?? ""); + var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(securityKeyBase64)); + + var jwtHander = new JwtSecurityTokenHandler(); + var claimsIdentity = jwtHander.ValidateToken(token, new TokenValidationParameters + { + ValidateIssuer = false, + ValidateAudience = false, + ValidateIssuerSigningKey = false, + ValidIssuer = "", + ValidAudience = "", + IssuerSigningKey = key, + ValidateLifetime = true, + + }, out _); + + var id = int.Parse(claimsIdentity.Claims.FirstOrDefault(x => x.Type == ClaimTypes.NameIdentifier)?.Value ?? "0"); + + var user = await _db.Users.FindAsync(id); + if (user == null) + { + throw new InvalidTokenException(); + } + var role = user.IsSystem ? IdentityRoleEnum.Admin : IdentityRoleEnum.User; + + return new ValidateCommandResponse(token, new IdentityInfo + { + Name = user.Name, + Id = id, + Role = role + }, _settings.TokenLifetimeMinutes); + } + catch (Exception ex) + { + throw new InvalidTokenException(); + } + + } + } +} diff --git a/Yes.Application/Admins/Configurations/GetConfigurationQueryHandler.cs b/Yes.Application/Admins/Configurations/GetConfigurationQueryHandler.cs index a76f420..a8c5ca7 100644 --- a/Yes.Application/Admins/Configurations/GetConfigurationQueryHandler.cs +++ b/Yes.Application/Admins/Configurations/GetConfigurationQueryHandler.cs @@ -18,6 +18,7 @@ string CategoryRoute, string SearchRoute, string TagRoute, + int TokenLifetimeMinutes, StorageSettingsResponse Storage ); diff --git a/Yes.Application/GlobalUsings.cs b/Yes.Application/GlobalUsings.cs index 39923ff..eebf572 100644 --- a/Yes.Application/GlobalUsings.cs +++ b/Yes.Application/GlobalUsings.cs @@ -32,4 +32,8 @@ global using Yes.Infrastructure.Authorizations.Identity.Context; global using Yes.Infrastructure.Authorizations.Identity; global using Microsoft.Data.SqlClient; global using Yes.Infrastructure.Migrator.Providers; -global using Yes.Infrastructure.Helpers; \ No newline at end of file +global using Yes.Infrastructure.Helpers; +global using System.Security.Claims; +global using System.IdentityModel.Tokens.Jwt; +global using Microsoft.IdentityModel.Tokens; +global using System.Text; \ No newline at end of file diff --git a/Yes.Blog/Areas/Admin/Pages/Article/Add.cshtml.cs b/Yes.Blog/Areas/Admin/Pages/Article/Add.cshtml.cs index ca552cc..71562d8 100644 --- a/Yes.Blog/Areas/Admin/Pages/Article/Add.cshtml.cs +++ b/Yes.Blog/Areas/Admin/Pages/Article/Add.cshtml.cs @@ -1,6 +1,6 @@ namespace Yes.Blog.Areas.Admin.Pages.Article { - public class AddModel : PageModel + public class AddModel : BasePageModel { } diff --git a/Yes.Blog/Areas/Admin/Pages/Article/Detail.cshtml.cs b/Yes.Blog/Areas/Admin/Pages/Article/Detail.cshtml.cs index 49115c8..a71b9ee 100644 --- a/Yes.Blog/Areas/Admin/Pages/Article/Detail.cshtml.cs +++ b/Yes.Blog/Areas/Admin/Pages/Article/Detail.cshtml.cs @@ -1,6 +1,6 @@ namespace Yes.Blog.Areas.Admin.Pages.Article { - public class DetailModel : PageModel + public class DetailModel : BasePageModel { diff --git a/Yes.Blog/Areas/Admin/Pages/Article/Index.cshtml.cs b/Yes.Blog/Areas/Admin/Pages/Article/Index.cshtml.cs index 9d84060..86fa17c 100644 --- a/Yes.Blog/Areas/Admin/Pages/Article/Index.cshtml.cs +++ b/Yes.Blog/Areas/Admin/Pages/Article/Index.cshtml.cs @@ -1,6 +1,6 @@ namespace Yes.Blog.Areas.Admin.Pages.Article { - public class IndexModel : PageModel + public class IndexModel : BasePageModel { } diff --git a/Yes.Blog/Areas/Admin/Pages/BasePageModel.cs b/Yes.Blog/Areas/Admin/Pages/BasePageModel.cs new file mode 100644 index 0000000..45bceb5 --- /dev/null +++ b/Yes.Blog/Areas/Admin/Pages/BasePageModel.cs @@ -0,0 +1,11 @@ + + + +namespace Yes.Blog.Areas.Admin.Pages +{ + + [PageAuthorize] + public class BasePageModel : PageModel + { + } +} diff --git a/Yes.Blog/Areas/Admin/Pages/Category/Add.cshtml.cs b/Yes.Blog/Areas/Admin/Pages/Category/Add.cshtml.cs index b3c133f..76c5b22 100644 --- a/Yes.Blog/Areas/Admin/Pages/Category/Add.cshtml.cs +++ b/Yes.Blog/Areas/Admin/Pages/Category/Add.cshtml.cs @@ -1,6 +1,6 @@ namespace Yes.Blog.Areas.Admin.Pages.Category { - public class AddModel : PageModel + public class AddModel : BasePageModel { } diff --git a/Yes.Blog/Areas/Admin/Pages/Category/Detail.cshtml.cs b/Yes.Blog/Areas/Admin/Pages/Category/Detail.cshtml.cs index 7b05cea..0f85134 100644 --- a/Yes.Blog/Areas/Admin/Pages/Category/Detail.cshtml.cs +++ b/Yes.Blog/Areas/Admin/Pages/Category/Detail.cshtml.cs @@ -1,6 +1,6 @@ namespace Yes.Blog.Areas.Admin.Pages.Category { - public class DetailModel : PageModel + public class DetailModel : BasePageModel { } diff --git a/Yes.Blog/Areas/Admin/Pages/Category/Index.cshtml.cs b/Yes.Blog/Areas/Admin/Pages/Category/Index.cshtml.cs index da1b5bf..84848a8 100644 --- a/Yes.Blog/Areas/Admin/Pages/Category/Index.cshtml.cs +++ b/Yes.Blog/Areas/Admin/Pages/Category/Index.cshtml.cs @@ -1,7 +1,7 @@ namespace Yes.Blog.Areas.Admin.Pages.Category { - public class IndexModel : PageModel + public class IndexModel : BasePageModel { public void OnGet() { diff --git a/Yes.Blog/Areas/Admin/Pages/Comment/Index.cshtml.cs b/Yes.Blog/Areas/Admin/Pages/Comment/Index.cshtml.cs index 6b0d650..962b796 100644 --- a/Yes.Blog/Areas/Admin/Pages/Comment/Index.cshtml.cs +++ b/Yes.Blog/Areas/Admin/Pages/Comment/Index.cshtml.cs @@ -1,6 +1,6 @@ namespace Yes.Blog.Areas.Admin.Pages.Comment { - public class IndexModel : PageModel + public class IndexModel : BasePageModel { public void OnGet() { diff --git a/Yes.Blog/Areas/Admin/Pages/Configuration/Index.cshtml b/Yes.Blog/Areas/Admin/Pages/Configuration/Index.cshtml index f59a74d..6e760d9 100644 --- a/Yes.Blog/Areas/Admin/Pages/Configuration/Index.cshtml +++ b/Yes.Blog/Areas/Admin/Pages/Configuration/Index.cshtml @@ -31,6 +31,7 @@
  • 显示设置
  • 固定链接设置
  • 文件存储
  • +
  • 安全
  • @@ -210,6 +211,15 @@
    + +
    + +
    + + +
    + +
    diff --git a/Yes.Blog/Areas/Admin/Pages/Configuration/Index.cshtml.cs b/Yes.Blog/Areas/Admin/Pages/Configuration/Index.cshtml.cs index 479ca8d..cb9b8eb 100644 --- a/Yes.Blog/Areas/Admin/Pages/Configuration/Index.cshtml.cs +++ b/Yes.Blog/Areas/Admin/Pages/Configuration/Index.cshtml.cs @@ -1,6 +1,6 @@ namespace Yes.Blog.Areas.Admin.Pages.Configuration { - public class IndexModel : PageModel + public class IndexModel : BasePageModel { } diff --git a/Yes.Blog/Areas/Admin/Pages/Error.cshtml.cs b/Yes.Blog/Areas/Admin/Pages/Error.cshtml.cs index 2781ac6..d8fe7d5 100644 --- a/Yes.Blog/Areas/Admin/Pages/Error.cshtml.cs +++ b/Yes.Blog/Areas/Admin/Pages/Error.cshtml.cs @@ -1,7 +1,7 @@ namespace Yes.Blog.Areas.Admin.Pages { - public class ErrorModel : PageModel + public class ErrorModel : BasePageModel { public void OnGet() { diff --git a/Yes.Blog/Areas/Admin/Pages/Index.cshtml.cs b/Yes.Blog/Areas/Admin/Pages/Index.cshtml.cs index ac3bdbb..674f20c 100644 --- a/Yes.Blog/Areas/Admin/Pages/Index.cshtml.cs +++ b/Yes.Blog/Areas/Admin/Pages/Index.cshtml.cs @@ -1,6 +1,6 @@ namespace Yes.Blog.Areas.Admin.Pages { - public class IndexModel : PageModel + public class IndexModel : BasePageModel { } diff --git a/Yes.Blog/Areas/Admin/Pages/Login.cshtml.cs b/Yes.Blog/Areas/Admin/Pages/Login.cshtml.cs index f84763a..5452da8 100644 --- a/Yes.Blog/Areas/Admin/Pages/Login.cshtml.cs +++ b/Yes.Blog/Areas/Admin/Pages/Login.cshtml.cs @@ -1,7 +1,7 @@ namespace Yes.Blog.Areas.Admin.Pages { - public class LoginModel : PageModel + public class LoginModel : BasePageModel { } diff --git a/Yes.Blog/Areas/Admin/Pages/Page/Add.cshtml.cs b/Yes.Blog/Areas/Admin/Pages/Page/Add.cshtml.cs index 6229ff6..dfa6ab7 100644 --- a/Yes.Blog/Areas/Admin/Pages/Page/Add.cshtml.cs +++ b/Yes.Blog/Areas/Admin/Pages/Page/Add.cshtml.cs @@ -1,6 +1,6 @@ namespace Yes.Blog.Areas.Admin.Pages.Page { - public class AddModel : PageModel + public class AddModel : BasePageModel { } diff --git a/Yes.Blog/Areas/Admin/Pages/Page/Detail.cshtml.cs b/Yes.Blog/Areas/Admin/Pages/Page/Detail.cshtml.cs index 9261844..2dfa024 100644 --- a/Yes.Blog/Areas/Admin/Pages/Page/Detail.cshtml.cs +++ b/Yes.Blog/Areas/Admin/Pages/Page/Detail.cshtml.cs @@ -1,6 +1,6 @@ namespace Yes.Blog.Areas.Admin.Pages.Page { - public class DetailModel : PageModel + public class DetailModel : BasePageModel { } diff --git a/Yes.Blog/Areas/Admin/Pages/Page/Index.cshtml.cs b/Yes.Blog/Areas/Admin/Pages/Page/Index.cshtml.cs index 1a7f55d..5eb9088 100644 --- a/Yes.Blog/Areas/Admin/Pages/Page/Index.cshtml.cs +++ b/Yes.Blog/Areas/Admin/Pages/Page/Index.cshtml.cs @@ -1,6 +1,6 @@ namespace Yes.Blog.Areas.Admin.Pages.Page { - public class IndexModel : PageModel + public class IndexModel : BasePageModel { } diff --git a/Yes.Blog/Areas/Admin/Pages/Tag/Detail.cshtml.cs b/Yes.Blog/Areas/Admin/Pages/Tag/Detail.cshtml.cs index 8ed4d17..bd65b96 100644 --- a/Yes.Blog/Areas/Admin/Pages/Tag/Detail.cshtml.cs +++ b/Yes.Blog/Areas/Admin/Pages/Tag/Detail.cshtml.cs @@ -1,6 +1,6 @@ namespace Yes.Blog.Areas.Admin.Pages.Tag { - public class DetailModel : PageModel + public class DetailModel : BasePageModel { public void OnGet() { diff --git a/Yes.Blog/Areas/Admin/Pages/Tag/Index.cshtml.cs b/Yes.Blog/Areas/Admin/Pages/Tag/Index.cshtml.cs index 84a9dfb..153bfd0 100644 --- a/Yes.Blog/Areas/Admin/Pages/Tag/Index.cshtml.cs +++ b/Yes.Blog/Areas/Admin/Pages/Tag/Index.cshtml.cs @@ -1,6 +1,6 @@ namespace Yes.Blog.Areas.Admin.Pages.Tag { - public class IndexModel : PageModel + public class IndexModel : BasePageModel { public void OnGet() { diff --git a/Yes.Blog/Areas/Admin/Pages/Theme/Change.cshtml.cs b/Yes.Blog/Areas/Admin/Pages/Theme/Change.cshtml.cs index 69845ab..180ba35 100644 --- a/Yes.Blog/Areas/Admin/Pages/Theme/Change.cshtml.cs +++ b/Yes.Blog/Areas/Admin/Pages/Theme/Change.cshtml.cs @@ -1,6 +1,6 @@ namespace Yes.Blog.Areas.Admin.Pages.Theme { - public class ChangeModel : PageModel + public class ChangeModel : BasePageModel { } diff --git a/Yes.Blog/Areas/Admin/Pages/Theme/Detail.cshtml.cs b/Yes.Blog/Areas/Admin/Pages/Theme/Detail.cshtml.cs index c5e3f50..2becebf 100644 --- a/Yes.Blog/Areas/Admin/Pages/Theme/Detail.cshtml.cs +++ b/Yes.Blog/Areas/Admin/Pages/Theme/Detail.cshtml.cs @@ -1,6 +1,6 @@ namespace Yes.Blog.Areas.Admin.Pages.Theme { - public class DetailModel : PageModel + public class DetailModel : BasePageModel { public void OnGet() { diff --git a/Yes.Blog/Areas/Admin/Pages/Theme/Import.cshtml.cs b/Yes.Blog/Areas/Admin/Pages/Theme/Import.cshtml.cs index 6172e6b..a1286aa 100644 --- a/Yes.Blog/Areas/Admin/Pages/Theme/Import.cshtml.cs +++ b/Yes.Blog/Areas/Admin/Pages/Theme/Import.cshtml.cs @@ -1,6 +1,6 @@ namespace Yes.Blog.Areas.Admin.Pages.Theme { - public class ImportModel : PageModel + public class ImportModel : BasePageModel { public void OnGet() { diff --git a/Yes.Blog/Areas/Admin/Pages/Theme/Index.cshtml.cs b/Yes.Blog/Areas/Admin/Pages/Theme/Index.cshtml.cs index 2638c9a..e7f6804 100644 --- a/Yes.Blog/Areas/Admin/Pages/Theme/Index.cshtml.cs +++ b/Yes.Blog/Areas/Admin/Pages/Theme/Index.cshtml.cs @@ -1,6 +1,6 @@ namespace Yes.Blog.Areas.Admin.Pages.Theme { - public class IndexModel : PageModel + public class IndexModel : BasePageModel { } diff --git a/Yes.Blog/Areas/Admin/Pages/User/Add.cshtml.cs b/Yes.Blog/Areas/Admin/Pages/User/Add.cshtml.cs index 3a5a8fe..68670fc 100644 --- a/Yes.Blog/Areas/Admin/Pages/User/Add.cshtml.cs +++ b/Yes.Blog/Areas/Admin/Pages/User/Add.cshtml.cs @@ -1,6 +1,6 @@ namespace Yes.Blog.Areas.Admin.Pages.User { - public class AddModel : PageModel + public class AddModel : BasePageModel { } diff --git a/Yes.Blog/Areas/Admin/Pages/User/Detail.cshtml.cs b/Yes.Blog/Areas/Admin/Pages/User/Detail.cshtml.cs index 2e03904..3f0cc25 100644 --- a/Yes.Blog/Areas/Admin/Pages/User/Detail.cshtml.cs +++ b/Yes.Blog/Areas/Admin/Pages/User/Detail.cshtml.cs @@ -1,6 +1,6 @@ namespace Yes.Blog.Areas.Admin.Pages.User { - public class DetailModel : PageModel + public class DetailModel : BasePageModel { } diff --git a/Yes.Blog/Areas/Admin/Pages/User/Index.cshtml.cs b/Yes.Blog/Areas/Admin/Pages/User/Index.cshtml.cs index 5f225e9..8b688a9 100644 --- a/Yes.Blog/Areas/Admin/Pages/User/Index.cshtml.cs +++ b/Yes.Blog/Areas/Admin/Pages/User/Index.cshtml.cs @@ -1,6 +1,6 @@ namespace Yes.Blog.Areas.Admin.Pages.User { - public class IndexModel : PageModel + public class IndexModel : BasePageModel { } diff --git a/Yes.Blog/Areas/Admin/Pages/User/Password.cshtml.cs b/Yes.Blog/Areas/Admin/Pages/User/Password.cshtml.cs index 044fbd8..8913699 100644 --- a/Yes.Blog/Areas/Admin/Pages/User/Password.cshtml.cs +++ b/Yes.Blog/Areas/Admin/Pages/User/Password.cshtml.cs @@ -1,6 +1,6 @@ namespace Yes.Blog.Areas.Admin.Pages.User { - public class PasswordModel : PageModel + public class PasswordModel : BasePageModel { public void OnGet() { diff --git a/Yes.Blog/Areas/Admin/Pages/User/Profile.cshtml.cs b/Yes.Blog/Areas/Admin/Pages/User/Profile.cshtml.cs index 530a360..85b3fb9 100644 --- a/Yes.Blog/Areas/Admin/Pages/User/Profile.cshtml.cs +++ b/Yes.Blog/Areas/Admin/Pages/User/Profile.cshtml.cs @@ -1,6 +1,6 @@ namespace Yes.Blog.Areas.Admin.Pages.User { - public class ProfileModel : PageModel + public class ProfileModel : BasePageModel { } diff --git a/Yes.Blog/Areas/Install/Pages/Index.cshtml.cs b/Yes.Blog/Areas/Install/Pages/Index.cshtml.cs index ac962a2..e266383 100644 --- a/Yes.Blog/Areas/Install/Pages/Index.cshtml.cs +++ b/Yes.Blog/Areas/Install/Pages/Index.cshtml.cs @@ -1,8 +1,10 @@ + + namespace Yes.Blog.Areas.Install.Pages { - public class IndexModel(IOptionsMonitor options, IWebHostEnvironment env, IFileService fileService) : PageModel + public class IndexModel(IOptionsMonitor options, IWebHostEnvironment env, IFileService fileService) : BasePageModel { public IActionResult OnGet() { diff --git a/Yes.Blog/DependencyInjection.cs b/Yes.Blog/DependencyInjection.cs index 007be7e..a8aa280 100644 --- a/Yes.Blog/DependencyInjection.cs +++ b/Yes.Blog/DependencyInjection.cs @@ -8,7 +8,9 @@ public static IServiceCollection RegisterBlogModule(this IServiceCollection services, WebApplicationBuilder builder) { var env = builder.Environment; + var configuration = builder.BuildConfiguration(); + services.AddConfiguration(configuration); services.AddRazorPages(); services.AddEndpoints(); services.AddViewEngine(env); @@ -20,11 +22,9 @@ services.AddMediatR(); services.AddHttpClient(); - services.AddConfiguration(builder); - services.AddHttpContextAccessor(); - services.AddUserAuthentication(); + services.AddAdminAuthentication(configuration); services.AddAuthorization(); @@ -50,8 +50,7 @@ return services; } - - public static IServiceCollection AddConfiguration(this IServiceCollection services, WebApplicationBuilder builder) + private static IConfiguration BuildConfiguration(this WebApplicationBuilder builder) { var basePath = Path.Combine(builder.Environment.ContentRootPath, "files", "config"); @@ -67,6 +66,13 @@ .AddJsonFile($"appsettings.{builder.Environment.EnvironmentName}.json", optional: true) .Build(); + return configuration; + } + + + public static IServiceCollection AddConfiguration(this IServiceCollection services, IConfiguration configuration) + { + services.Configure(configuration); return services; @@ -220,10 +226,16 @@ public static IApplicationBuilder MapEndpoints(this WebApplication app) { + var apiPolicy = new AuthorizationPolicyBuilder() + .AddAuthenticationSchemes(AuthenticationScheme.ApiScheme) + .RequireAuthenticatedUser() + .Build(); + + var endpoints = app.Services.GetRequiredService>().OrderByDescending(x => x.Priority); foreach (var route in endpoints) { - var routeGroup = app.MapGroup($"/{route.Prefix}").WithTags(route.Tags); + var routeGroup = app.MapGroup($"/{route.Prefix}").WithTags(route.Tags).RequireAuthorization(); route.Map(routeGroup); } diff --git a/Yes.Blog/Endpoints/Admins/AdminEndpointScheme.cs b/Yes.Blog/Endpoints/Admins/AdminEndpointScheme.cs deleted file mode 100644 index 41c4269..0000000 --- a/Yes.Blog/Endpoints/Admins/AdminEndpointScheme.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace Yes.Blog.Endpoints.Admins -{ - public class AdminEndpointScheme - { - public int Priority { get; set; } = 3; - public string Prefix { get; set; } = "admin/api"; - - public string[] Tags { get; set; } = new[] { "Blog" }; - } -} diff --git a/Yes.Blog/Endpoints/Admins/Auths/UserLoginEndpoint.cs b/Yes.Blog/Endpoints/Admins/Auths/UserLoginEndpoint.cs index 0b7f454..4e3d95f 100644 --- a/Yes.Blog/Endpoints/Admins/Auths/UserLoginEndpoint.cs +++ b/Yes.Blog/Endpoints/Admins/Auths/UserLoginEndpoint.cs @@ -1,9 +1,11 @@ -namespace Yes.Blog.Endpoints.Admins.Auths +using Yes.Infrastructure.Authorizations; + +namespace Yes.Blog.Endpoints.Admins.Auths { public class UserLoginEndpoint : AuthEndpointScheme, IEndpoint { - public void Map(IEndpointRouteBuilder app) => app.MapPost("/login", Handle).WithRequestValidation(); + public void Map(IEndpointRouteBuilder app) => app.MapPost("/login", Handle).WithRequestValidation().AllowAnonymous(); public record Request(string Name, string Password); @@ -21,7 +23,7 @@ var command = mapper.Map(request); var response = await mediator.Send(command, cancellationToken); - await context.SignIn(response.Identity); + await context.SignIn(response.Identity, response.TokenLifetimeMinutes); return Result.Ok(response); } diff --git a/Yes.Blog/Endpoints/Admins/Auths/UserLogoutEndpoint.cs b/Yes.Blog/Endpoints/Admins/Auths/UserLogoutEndpoint.cs index 19e7ab0..1522e82 100644 --- a/Yes.Blog/Endpoints/Admins/Auths/UserLogoutEndpoint.cs +++ b/Yes.Blog/Endpoints/Admins/Auths/UserLogoutEndpoint.cs @@ -3,7 +3,7 @@ public class UserLogoutEndpoint : AuthEndpointScheme, IEndpoint { - public void Map(IEndpointRouteBuilder app) => app.MapPost("/logout", Handle); + public void Map(IEndpointRouteBuilder app) => app.MapPost("/logout", Handle).AllowAnonymous(); private async Task Handle(IMediator mediator, IMapper mapper, HttpContext context, CancellationToken cancellationToken) diff --git a/Yes.Blog/Endpoints/Admins/Auths/ValidateEndpoint.cs b/Yes.Blog/Endpoints/Admins/Auths/ValidateEndpoint.cs new file mode 100644 index 0000000..97c7538 --- /dev/null +++ b/Yes.Blog/Endpoints/Admins/Auths/ValidateEndpoint.cs @@ -0,0 +1,34 @@ +using Yes.Infrastructure.Authorizations; + +namespace Yes.Blog.Endpoints.Admins.Auths +{ + public class ValidateEndpoint : AuthEndpointScheme, IEndpoint + { + public void Map(IEndpointRouteBuilder app) => app.MapPost("/validate", Handle).AllowAnonymous(); + + + private async Task Handle([FromHeader] string authorization, + IMediator mediator, + IMapper mapper, + HttpContext context, + IOptionsMonitor options,CancellationToken cancellationToken) + { + var token = authorization?.Replace("Bearer ", ""); + + try + { + var command = new ValidateCommand(token); + var response = await mediator.Send(command, cancellationToken); + + await context.SignIn(response.Identity, response.TokenLifetimeMinutes); + + + return Result.Ok(new { IsValid = true }); + } + catch (Exception ex) + { + return Result.Ok(new { IsValid = false, Error = ex.Message }); + } + } + } +} diff --git a/Yes.Blog/Endpoints/Blogs/CreateCommentEndpoint.cs b/Yes.Blog/Endpoints/Blogs/CreateCommentEndpoint.cs index 9c8106a..220a934 100644 --- a/Yes.Blog/Endpoints/Blogs/CreateCommentEndpoint.cs +++ b/Yes.Blog/Endpoints/Blogs/CreateCommentEndpoint.cs @@ -2,7 +2,7 @@ { public class CreateCommentEndpoint : BlogEndpointScheme, IEndpoint { - public void Map(IEndpointRouteBuilder app) => app.MapPost("/comments", Handle).WithRequestValidation(); + public void Map(IEndpointRouteBuilder app) => app.MapPost("/comments", Handle).WithRequestValidation().AllowAnonymous(); public record Request( diff --git a/Yes.Blog/Endpoints/Blogs/IndexEndpoint.cs b/Yes.Blog/Endpoints/Blogs/IndexEndpoint.cs index 63fd2b8..862da1c 100644 --- a/Yes.Blog/Endpoints/Blogs/IndexEndpoint.cs +++ b/Yes.Blog/Endpoints/Blogs/IndexEndpoint.cs @@ -2,7 +2,7 @@ { public class IndexEndpoint : BlogEndpointScheme, IEndpoint { - public void Map(IEndpointRouteBuilder app) => app.MapGet("/{**path}", Handle); + public void Map(IEndpointRouteBuilder app) => app.MapGet("/{**path}", Handle).AllowAnonymous(); private async Task Handle( IMediator mediator, diff --git a/Yes.Blog/Endpoints/Blogs/InstallEndpoint.cs b/Yes.Blog/Endpoints/Blogs/InstallEndpoint.cs index 59e6d7b..7cbe316 100644 --- a/Yes.Blog/Endpoints/Blogs/InstallEndpoint.cs +++ b/Yes.Blog/Endpoints/Blogs/InstallEndpoint.cs @@ -3,7 +3,7 @@ namespace Yes.Blog.Endpoints.Blogs { public class InstallEndpoint : BlogEndpointScheme, IEndpoint { - public void Map(IEndpointRouteBuilder app) => app.MapPost("install", Handle).WithRequestValidation(); + public void Map(IEndpointRouteBuilder app) => app.MapPost("install", Handle).WithRequestValidation().AllowAnonymous(); public record Request( string DatabaseType, diff --git a/Yes.Blog/Endpoints/Blogs/SearchEndpoint.cs b/Yes.Blog/Endpoints/Blogs/SearchEndpoint.cs index 3c55ca4..2c936c4 100644 --- a/Yes.Blog/Endpoints/Blogs/SearchEndpoint.cs +++ b/Yes.Blog/Endpoints/Blogs/SearchEndpoint.cs @@ -3,7 +3,7 @@ namespace Yes.Blog.Endpoints.Blogs { public class SearchEndpoint : BlogEndpointScheme, IEndpoint { - public void Map(IEndpointRouteBuilder app) => app.MapPost("/search", Handle).DisableAntiforgery(); + public void Map(IEndpointRouteBuilder app) => app.MapPost("/search", Handle).DisableAntiforgery().AllowAnonymous(); public record Request( diff --git a/Yes.Blog/Endpoints/Blogs/VersionEndpoint.cs b/Yes.Blog/Endpoints/Blogs/VersionEndpoint.cs index 2bebf2d..f77e529 100644 --- a/Yes.Blog/Endpoints/Blogs/VersionEndpoint.cs +++ b/Yes.Blog/Endpoints/Blogs/VersionEndpoint.cs @@ -3,7 +3,7 @@ public class VersionEndpoint : BlogEndpointScheme, IEndpoint { - public void Map(IEndpointRouteBuilder app) => app.MapGet("/version", Handle); + public void Map(IEndpointRouteBuilder app) => app.MapGet("/version", Handle).AllowAnonymous(); private string Handle( CancellationToken cancellationToken) diff --git a/Yes.Blog/GlobalUsings.cs b/Yes.Blog/GlobalUsings.cs index bda73a2..5348deb 100644 --- a/Yes.Blog/GlobalUsings.cs +++ b/Yes.Blog/GlobalUsings.cs @@ -42,6 +42,8 @@ global using Yes.Infrastructure.Authorizations; global using Yes.Infrastructure.Middlewares; global using Yes.Infrastructure.ViewEngine; global using IResult = Yes.Domain.Core.Models.IResult; - +global using Microsoft.AspNetCore.Authorization; global using Yes.Application.Installs; -global using Scalar.AspNetCore; \ No newline at end of file +global using Scalar.AspNetCore; +global using Yes.Blog.Areas.Admin.Pages; +global using Yes.Infrastructure.Authorizations.User; \ No newline at end of file diff --git a/Yes.Blog/appsettings.json b/Yes.Blog/appsettings.json index 10f68b8..13bcb9a 100644 --- a/Yes.Blog/appsettings.json +++ b/Yes.Blog/appsettings.json @@ -1,9 +1,15 @@ { - "Logging": { - "LogLevel": { - "Default": "Information", - "Microsoft.AspNetCore": "Warning" + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*", + "IdentityModel": { + "DisableTokenReplayValidation": true, + "PII": { + "LogSecurityArtifacts": true // 显示敏感错误详情 + } } - }, - "AllowedHosts": "*" } diff --git a/Yes.Blog/wwwroot/admin/js/login.js b/Yes.Blog/wwwroot/admin/js/login.js index 48d902b..a2447bb 100644 --- a/Yes.Blog/wwwroot/admin/js/login.js +++ b/Yes.Blog/wwwroot/admin/js/login.js @@ -5,15 +5,27 @@ name: "", password: "" }, + validate() { + axios.post('validate') + .then(data => { + console.log('data:', data); + if (data.isValid) { + location.href = "/admin"; + } + }) + }, login() { axios.post('login', this.user) .then(data => { + console.log('data:', data); localStorage.setItem("username", this.user.name); localStorage.setItem("token", data.token); location.href = "/admin"; }) }, init() { + this.validate(); + window.addEventListener('load', () => { document.title = "登录到 - " + window.config.name + ""; }); diff --git a/Yes.Blog/wwwroot/admin/js/main.js b/Yes.Blog/wwwroot/admin/js/main.js index 6e2243b..4910cc0 100644 --- a/Yes.Blog/wwwroot/admin/js/main.js +++ b/Yes.Blog/wwwroot/admin/js/main.js @@ -3,7 +3,7 @@ axios.defaults.baseURL = "/admin/api/" axios.defaults.timeout = 3000; - axios.defaults.headers.common['Authorization'] = ""; + axios.defaults.headers.common['Authorization'] = "Bearer " + localStorage.getItem("token")+""; axios.defaults.headers.post['Content-Type'] = 'application/json'; axios.interceptors.request.use( (config) => { diff --git a/Yes.Domain/Configurations/BlogSettings.cs b/Yes.Domain/Configurations/BlogSettings.cs index 7db2ccc..b599d7d 100644 --- a/Yes.Domain/Configurations/BlogSettings.cs +++ b/Yes.Domain/Configurations/BlogSettings.cs @@ -53,6 +53,12 @@ } }; + + + + public string SecretKey { get; set; } = Guid.NewGuid().ToString(); + + public int TokenLifetimeMinutes { get; set; } = 60 * 24 * 7; } diff --git a/Yes.Domain/Core/Exceptions/InvalidTokenException.cs b/Yes.Domain/Core/Exceptions/InvalidTokenException.cs new file mode 100644 index 0000000..df93e39 --- /dev/null +++ b/Yes.Domain/Core/Exceptions/InvalidTokenException.cs @@ -0,0 +1,11 @@ +namespace Yes.Domain.Core.Exceptions +{ + public class InvalidTokenException : BaseException + { + + public InvalidTokenException() : base($"登录失效,请重新登录!") + { + + } + } +} diff --git a/Yes.Infrastructure/Authorizations/AuthenticationExtensions.cs b/Yes.Infrastructure/Authorizations/AuthenticationExtensions.cs index 38e033b..cbbfea7 100644 --- a/Yes.Infrastructure/Authorizations/AuthenticationExtensions.cs +++ b/Yes.Infrastructure/Authorizations/AuthenticationExtensions.cs @@ -1,15 +1,16 @@ - - -namespace Yes.Infrastructure.Authorizations +namespace Yes.Infrastructure.Authorizations { public static class AuthenticationExtensions { - public static IServiceCollection AddUserAuthentication(this IServiceCollection services) + public static IServiceCollection AddAdminAuthentication(this IServiceCollection services,IConfiguration configuration) { - services.AddAuthentication(AuthenticationScheme.UserScheme) - .AddCookie(AuthenticationScheme.UserScheme, options => + var securityKeyBase64 = JwtHelper.GenerateKey(configuration["SecretKey"] ?? ""); + + + services.AddAuthentication(AuthenticationScheme.AdminScheme) + .AddCookie(AuthenticationScheme.AdminScheme, options => { - options.Cookie.Name = AuthenticationScheme.UserScheme; + options.Cookie.Name = AuthenticationScheme.AdminScheme; options.LoginPath = "/admin/login"; options.LogoutPath = "/admin/logout"; options.ExpireTimeSpan = TimeSpan.FromDays(3); @@ -18,7 +19,7 @@ namespace Yes.Infrastructure.Authorizations options.Events.OnRedirectToAccessDenied = options.Events.OnRedirectToLogin = c => { - if(c.Request.GetDisplayUrl().Contains("/api")) + if (c.Request.GetDisplayUrl().Contains("/api")) { c.Response.ContentType = "application/json"; c.Response.StatusCode = StatusCodes.Status401Unauthorized; @@ -30,18 +31,32 @@ namespace Yes.Infrastructure.Authorizations return Task.CompletedTask; } }; - }); + }).AddJwtBearer(AuthenticationScheme.ApiScheme, options => + { + options.TokenValidationParameters = new TokenValidationParameters + { + ValidateLifetime = true, + RequireSignedTokens = false, + RequireExpirationTime = false, + ValidateIssuer = true, + ValidateIssuerSigningKey = true, + ValidIssuer = "hyrule", + IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(securityKeyBase64)) + }; + }); return services; } - public static async Task SignIn(this HttpContext context, IdentityInfo account) + + + public static async Task SignIn(this HttpContext context, IdentityInfo account, int tokenLifetimeMinutes) { - await context.SignIn(AuthenticationScheme.UserScheme, account); + await context.SignIn(AuthenticationScheme.AdminScheme, account, tokenLifetimeMinutes); } - public static async Task SignIn(this HttpContext context, string authenticationScheme, IdentityInfo account) + public static async Task SignIn(this HttpContext context, string authenticationScheme, IdentityInfo account, int tokenLifetimeMinutes) { var claims = new List { @@ -59,7 +74,7 @@ namespace Yes.Infrastructure.Authorizations new AuthenticationProperties() { IsPersistent = true, - ExpiresUtc = DateTimeOffset.UtcNow.AddHours(24 * 30),//有效时间 + ExpiresUtc = DateTimeOffset.UtcNow.AddMinutes(tokenLifetimeMinutes),//有效时间 AllowRefresh = true } ); @@ -74,7 +89,7 @@ namespace Yes.Infrastructure.Authorizations public static async Task SignOut(this HttpContext context) { - await context.SignOutAsync(AuthenticationScheme.UserScheme); + await context.SignOutAsync(AuthenticationScheme.AdminScheme); } public static async Task HasLogin(this HttpContext context) diff --git a/Yes.Infrastructure/Authorizations/AuthenticationScheme.cs b/Yes.Infrastructure/Authorizations/AuthenticationScheme.cs index 71f92f2..6ecbce0 100644 --- a/Yes.Infrastructure/Authorizations/AuthenticationScheme.cs +++ b/Yes.Infrastructure/Authorizations/AuthenticationScheme.cs @@ -2,10 +2,11 @@ { public class AuthenticationScheme { - public const string MerchanScheme = "merchan"; + public const string AdminScheme = "admin"; + + public const string ApiScheme = "api"; + - public const string UserScheme = "user"; - public const string UserApiScheme = "userapi"; } } diff --git a/Yes.Infrastructure/Authorizations/JwtProvider.cs b/Yes.Infrastructure/Authorizations/JwtProvider.cs index 0f0abe8..04573c3 100644 --- a/Yes.Infrastructure/Authorizations/JwtProvider.cs +++ b/Yes.Infrastructure/Authorizations/JwtProvider.cs @@ -2,11 +2,11 @@ { public class JwtProvider : IJwtProvider { - private readonly string securityKey = ""; + private readonly BlogSettings _settings; - public JwtProvider(IConfiguration configuration) + public JwtProvider(IOptionsMonitor options) { - securityKey = configuration.GetSection("Jwt").GetValue("securityKey"); + _settings = options.CurrentValue; } @@ -15,16 +15,16 @@ { return await Task.Run(() => { - var securityKeyBase64 = Convert.ToBase64String(Encoding.UTF8.GetBytes(securityKey)); + var securityKeyBase64 = Convert.ToBase64String(Encoding.UTF8.GetBytes(_settings.SecretKey)); var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(securityKeyBase64)); var jwtHander = new JwtSecurityTokenHandler(); var claimsIdentity = jwtHander.ValidateToken(token, new TokenValidationParameters { - ValidateIssuer = false, + ValidateIssuer = true, ValidateAudience = false, ValidateIssuerSigningKey = true, - ValidIssuer = "", + ValidIssuer = "hyrule", ValidAudience = "", IssuerSigningKey = key, ValidateLifetime = true, diff --git a/Yes.Infrastructure/Authorizations/User/UserApiAuthorizeAttribute.cs b/Yes.Infrastructure/Authorizations/User/UserApiAuthorizeAttribute.cs index 869734e..efd30cd 100644 --- a/Yes.Infrastructure/Authorizations/User/UserApiAuthorizeAttribute.cs +++ b/Yes.Infrastructure/Authorizations/User/UserApiAuthorizeAttribute.cs @@ -2,11 +2,11 @@ { - public class UserApiAuthorizeAttribute : AuthorizeAttribute + public class ApiAuthorizeAttribute : AuthorizeAttribute { - public UserApiAuthorizeAttribute() + public ApiAuthorizeAttribute() { - AuthenticationSchemes = AuthenticationScheme.UserApiScheme; + AuthenticationSchemes = AuthenticationScheme.ApiScheme; } } } diff --git a/Yes.Infrastructure/Authorizations/User/UserAuthorizeAttribute.cs b/Yes.Infrastructure/Authorizations/User/UserAuthorizeAttribute.cs deleted file mode 100644 index 45b2abf..0000000 --- a/Yes.Infrastructure/Authorizations/User/UserAuthorizeAttribute.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace Yes.Infrastructure.Authorizations.User -{ - - - public class UserAuthorizeAttribute : AuthorizeAttribute - { - public UserAuthorizeAttribute() - { - AuthenticationSchemes = AuthenticationScheme.UserScheme; - } - } -} diff --git a/Yes.Infrastructure/Authorizations/User/UserPageAuthorizeAttribute.cs b/Yes.Infrastructure/Authorizations/User/UserPageAuthorizeAttribute.cs new file mode 100644 index 0000000..12734e6 --- /dev/null +++ b/Yes.Infrastructure/Authorizations/User/UserPageAuthorizeAttribute.cs @@ -0,0 +1,12 @@ +namespace Yes.Infrastructure.Authorizations.User +{ + + + public class PageAuthorizeAttribute : AuthorizeAttribute + { + public PageAuthorizeAttribute() + { + AuthenticationSchemes = "admin"; + } + } +} diff --git a/Yes.Infrastructure/GlobalUsings.cs b/Yes.Infrastructure/GlobalUsings.cs index 2c02d6b..861d532 100644 --- a/Yes.Infrastructure/GlobalUsings.cs +++ b/Yes.Infrastructure/GlobalUsings.cs @@ -48,4 +48,7 @@ global using Yes.Infrastructure.Authorizations.Identity.Authroizations; global using Yes.Infrastructure.Authorizations.Identity.Context; global using Yes.Infrastructure.Data.DbContexts; global using Yes.Infrastructure.Migrator.Migrations; -global using System.Security; \ No newline at end of file +global using System.Security; +global using System.Security.Cryptography; +global using Yes.Infrastructure.Helpers; +global using Microsoft.AspNetCore.Authentication.JwtBearer; \ No newline at end of file diff --git a/Yes.Infrastructure/Helpers/JwtHelper.cs b/Yes.Infrastructure/Helpers/JwtHelper.cs new file mode 100644 index 0000000..8b16333 --- /dev/null +++ b/Yes.Infrastructure/Helpers/JwtHelper.cs @@ -0,0 +1,42 @@ +namespace Yes.Infrastructure.Helpers +{ + public class JwtHelper + { + public static string GenerateKey(string input) + { + if (string.IsNullOrEmpty(input)) + { + input = "hyrule"; + }; + + using (var sha256 = SHA256.Create()) + { + // 使用 UTF-8 编码输入字符串 + byte[] inputBytes = Encoding.UTF8.GetBytes(input); + + // 计算 SHA256 哈希值 + byte[] hashBytes = sha256.ComputeHash(inputBytes); + + // 将哈希结果转换为 Base64 字符串 + return Convert.ToBase64String(hashBytes); + } + } + + + public static string GenerateToken(Claim[] claims, string secretKey, int tokenLifetimeMinutes) + { + var securityKeyBase64 = JwtHelper.GenerateKey(secretKey ?? ""); + + var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(securityKeyBase64)); + var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256); + + var token = new JwtSecurityToken( + issuer: "hyrule", + claims: claims, + expires: DateTime.Now.AddMinutes(tokenLifetimeMinutes), + signingCredentials: creds + ); + return new JwtSecurityTokenHandler().WriteToken(token); + } + } +} diff --git a/Yes.Infrastructure/Yes.Infrastructure.csproj b/Yes.Infrastructure/Yes.Infrastructure.csproj index 31d36da..ba52a31 100644 --- a/Yes.Infrastructure/Yes.Infrastructure.csproj +++ b/Yes.Infrastructure/Yes.Infrastructure.csproj @@ -11,6 +11,7 @@ +