/*********************************************************************** * Project: CoreCms * ProjectName: 核心内容管理系统 * Web: https://www.corecms.net * Author: 大灰灰 * Email: jianweie@163.com * CreateTime: 2021/1/31 21:45:10 * Description: 暂无 ***********************************************************************/ using System; using System.Collections.Generic; using System.IdentityModel.Tokens.Jwt; using System.Linq; using System.Security.Claims; using System.Text; using System.Threading.Tasks; using CoreCms.Net.Auth.Policys; using CoreCms.Net.Configuration; using CoreCms.Net.Utility.Extensions; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; using Microsoft.IdentityModel.Tokens; namespace CoreCms.Net.Auth { /// /// Db 启动服务 /// public static class AuthorizationSetup { /// /// 后台管理员jwt初始化设置 /// /// public static void AddAuthorizationSetupForAdmin(this IServiceCollection services) { if (services == null) throw new ArgumentNullException(nameof(services)); #region 参数 //读取配置文件 var symmetricKeyAsBase64 = AppSettingsHelper.GetMachineRandomKey(AppSettingsConstVars.JwtConfigSecretKey); var keyByteArray = Encoding.ASCII.GetBytes(symmetricKeyAsBase64); var signingKey = new SymmetricSecurityKey(keyByteArray); var issuer = AppSettingsConstVars.JwtConfigIssuer; var audience = AppSettingsConstVars.JwtConfigAudience; var signingCredentials = new SigningCredentials(signingKey, SecurityAlgorithms.HmacSha256); // 如果要数据库动态绑定,这里先留个空,后边处理器里动态赋值 var permission = new List(); // 角色与接口的权限要求参数 var permissionRequirement = new PermissionRequirement( "/api/denied",// 拒绝授权的跳转地址(目前无用) permission, ClaimTypes.Role,//基于角色的授权 issuer,//发行人 audience,//听众 signingCredentials,//签名凭据 expiration: TimeSpan.FromSeconds(60 * 60 * 24)//接口的过期时间 ); #endregion // 复杂的策略授权 services.AddAuthorization(options => { options.AddPolicy(Permissions.Name, policy => policy.Requirements.Add(permissionRequirement)); }); // 令牌验证参数 var tokenValidationParameters = new TokenValidationParameters { ValidateIssuerSigningKey = true, //是否验证SecurityKey IssuerSigningKey = signingKey, //拿到SecurityKey ValidateIssuer = true, //是否验证Issuer ValidIssuer = issuer,//发行人 //Issuer,这两项和前面签发jwt的设置一致 ValidateAudience = true, //是否验证Audience ValidAudience = audience,//订阅人 ValidateLifetime = true,//是否验证失效时间 ClockSkew = TimeSpan.FromSeconds(60), RequireExpirationTime = true, }; // core自带官方JWT认证,开启Bearer认证 services.AddAuthentication(o => { o.DefaultScheme = JwtBearerDefaults.AuthenticationScheme; o.DefaultChallengeScheme = nameof(ApiResponseForAdminHandler); o.DefaultForbidScheme = nameof(ApiResponseForAdminHandler); }) // 添加JwtBearer服务 .AddJwtBearer(o => { o.TokenValidationParameters = tokenValidationParameters; o.Events = new JwtBearerEvents { OnChallenge = context => { context.Response.Headers.Append("Token-Error", context.ErrorDescription); return Task.CompletedTask; }, OnAuthenticationFailed = context => { var token = context.Request.Headers["Authorization"].ObjectToString().Replace("Bearer ", ""); var jwtToken = (new JwtSecurityTokenHandler()).ReadJwtToken(token); if (jwtToken.Issuer != issuer) { context.Response.Headers.Append("Token-Error-Iss", "issuer is wrong!"); } if (jwtToken.Audiences.FirstOrDefault() != audience) { context.Response.Headers.Append("Token-Error-Aud", "Audience is wrong!"); } // 如果过期,则把<是否过期>添加到,返回头信息中 if (context.Exception.GetType() == typeof(SecurityTokenExpiredException)) { context.Response.Headers.Append("Token-Expired", "true"); } return Task.CompletedTask; } }; }) .AddScheme(nameof(ApiResponseForAdminHandler), o => { }); // 注入权限处理器 services.AddScoped(); services.AddSingleton(permissionRequirement); } /// /// 前端客户jwt初始化设置 /// /// public static void AddAuthorizationSetupForClient(this IServiceCollection services) { if (services == null) throw new ArgumentNullException(nameof(services)); #region 参数 //读取配置文件 var symmetricKeyAsBase64 = AppSettingsConstVars.JwtConfigSecretKey; var keyByteArray = Encoding.ASCII.GetBytes(symmetricKeyAsBase64); var signingKey = new SymmetricSecurityKey(keyByteArray); var issuer = AppSettingsConstVars.JwtConfigIssuer; var audience = AppSettingsConstVars.JwtConfigAudience; // 👇 下面是你应该添加的打印代码,用于调试 Console.WriteLine("[JWT Debug] JwtConfigSecretKey 配置读取开始 ====================="); // 1. 打印密钥的长度(帮助判断是否为空或过短) Console.WriteLine($"[JWT Debug] JwtConfigSecretKey 长度: {symmetricKeyAsBase64.Length}"); // 2. 打印密钥的前 5 个字符(避免泄露全部密钥,但可以用于对比) if (!string.IsNullOrEmpty(symmetricKeyAsBase64) && symmetricKeyAsBase64.Length >= 5) { Console.WriteLine($"[JWT Debug] JwtConfigSecretKey 前5位: '{symmetricKeyAsBase64.Substring(0, 5)}...'"); } else { Console.WriteLine($"[JWT Debug] JwtConfigSecretKey 前5位: (太短或为空)"); } // 3. 打印密钥的 SHA256 哈希值(可用于绝对安全地对比密钥是否一致,不会泄露原文) try { var sha256Hash = System.Security.Cryptography.SHA256.HashData(keyByteArray); var hashString = BitConverter.ToString(sha256Hash).Replace("-", "").ToLower(); Console.WriteLine($"[JWT Debug] JwtConfigSecretKey SHA256 哈希值: {hashString},明文:{symmetricKeyAsBase64}"); } catch (Exception ex) { Console.WriteLine($"[JWT Debug] 计算密钥哈希时出错: {ex.Message}"); } Console.WriteLine("[JWT Debug] JwtConfigSecretKey 配置读取结束 ====================="); var signingCredentials = new SigningCredentials(signingKey, SecurityAlgorithms.HmacSha256); // 如果要数据库动态绑定,这里先留个空,后边处理器里动态赋值 var permission = new List(); // 角色与接口的权限要求参数 var permissionRequirement = new PermissionRequirement( "/api/denied",// 拒绝授权的跳转地址(目前无用) permission, ClaimTypes.Role,//基于角色的授权 issuer,//发行人 audience,//听众 signingCredentials,//签名凭据 expiration: TimeSpan.FromMinutes(60 * 2)//接口的过期时间 ); #endregion // 复杂的策略授权 services.AddAuthorization(options => { options.AddPolicy(Permissions.Name, policy => policy.Requirements.Add(permissionRequirement)); }); // 令牌验证参数 var tokenValidationParameters = new TokenValidationParameters { ValidateIssuerSigningKey = true, //是否验证SecurityKey IssuerSigningKey = signingKey, //拿到SecurityKey ValidateIssuer = true, //是否验证Issuer ValidIssuer = issuer,//发行人 ValidateAudience = true, //是否验证Audience ValidAudience = audience,//订阅人 ValidateLifetime = true, //是否验证失效时间 ClockSkew = TimeSpan.FromMinutes(60 * 1), RequireExpirationTime = true, }; // core自带官方JWT认证,开启Bearer认证 services.AddAuthentication(o => { o.DefaultScheme = JwtBearerDefaults.AuthenticationScheme; o.DefaultChallengeScheme = nameof(ApiResponseForClientHandler); o.DefaultForbidScheme = nameof(ApiResponseForClientHandler); }) // 添加JwtBearer服务 .AddJwtBearer(o => { o.TokenValidationParameters = tokenValidationParameters; o.Events = new JwtBearerEvents { OnChallenge = context => { context.Response.Headers.Append("Token-Error", context.ErrorDescription); return Task.CompletedTask; }, OnAuthenticationFailed = context => { var token = context.Request.Headers["Authorization"].ObjectToString().Replace("Bearer ", ""); var jwtToken = (new JwtSecurityTokenHandler()).ReadJwtToken(token); if (jwtToken.Issuer != issuer) { context.Response.Headers.Append("Token-Error-Iss", "issuer is wrong!"); } if (jwtToken.Audiences.FirstOrDefault() != audience) { context.Response.Headers.Append("Token-Error-Aud", "Audience is wrong!"); } // 如果过期,则把<是否过期>添加到,返回头信息中 if (context.Exception.GetType() == typeof(SecurityTokenExpiredException)) { context.Response.Headers.Append("Token-Expired", "true"); } return Task.CompletedTask; } }; }) .AddScheme(nameof(ApiResponseForClientHandler), o => { }); // 注入权限处理器 services.AddScoped(); services.AddSingleton(permissionRequirement); } } }