refactor(auth): 重构JWT认证相关代码,优化声明获取逻辑
1. 提取通用的GetClaim方法简化多声明类型查找逻辑 2. 重构JWT认证配置代码,拆分配置逻辑到单独方法 3. 优化开发环境下的认证策略,支持无认证和JWT认证自动切换
This commit is contained in:
@ -119,7 +119,7 @@ public static class JwtHelper
|
||||
var tokenHandler = new JwtSecurityTokenHandler();
|
||||
if (tokenHandler.ReadToken(token) is JwtSecurityToken jwtToken)
|
||||
{
|
||||
var userIdClaim = jwtToken.Claims.FirstOrDefault(c => c.Type == ClaimTypes.NameIdentifier);
|
||||
var userIdClaim = GetClaim(jwtToken, ClaimTypes.NameIdentifier, JwtRegisteredClaimNames.NameId, JwtRegisteredClaimNames.Sub);
|
||||
if (long.TryParse(userIdClaim?.Value, out long userId))
|
||||
{
|
||||
return userId;
|
||||
@ -157,8 +157,13 @@ public static class JwtHelper
|
||||
var tokenHandler = new JwtSecurityTokenHandler();
|
||||
if (tokenHandler.ReadToken(token) is JwtSecurityToken jwtToken)
|
||||
{
|
||||
return jwtToken.Claims.FirstOrDefault(c => c.Type == ClaimTypes.Name)?.Value ?? string.Empty;
|
||||
return GetClaim(jwtToken, ClaimTypes.Name, JwtRegisteredClaimNames.UniqueName, JwtRegisteredClaimNames.Name)?.Value ?? string.Empty;
|
||||
}
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
private static Claim? GetClaim(JwtSecurityToken jwtToken, params string[] claimTypes)
|
||||
{
|
||||
return jwtToken.Claims.FirstOrDefault(c => claimTypes.Contains(c.Type));
|
||||
}
|
||||
}
|
||||
|
||||
@ -21,6 +21,9 @@ namespace QYZH.InteractiveMagazine.Infrastructure.Extensions;
|
||||
/// </summary>
|
||||
public static class DependencyInjectionExtensions
|
||||
{
|
||||
private const string NoAuthScheme = "NoAuth";
|
||||
private const string DevelopmentAuthScheme = "DevelopmentSmartAuth";
|
||||
|
||||
/// <summary>
|
||||
/// Registers infrastructure services.
|
||||
/// </summary>
|
||||
@ -34,70 +37,90 @@ public static class DependencyInjectionExtensions
|
||||
|
||||
private static void AddJwtAuthentication(IServiceCollection services, IConfiguration configuration, IWebHostEnvironment? environment = null)
|
||||
{
|
||||
var jwtSettings = configuration.GetSection("JwtSettings").Get<JwtSettings>()!;
|
||||
services.AddSingleton(jwtSettings);
|
||||
|
||||
if (environment?.IsDevelopment() == true)
|
||||
{
|
||||
services.AddAuthentication("NoAuth")
|
||||
.AddScheme<AuthenticationSchemeOptions, NoAuthHandler>("NoAuth", options => { });
|
||||
services.AddAuthentication(options =>
|
||||
{
|
||||
options.DefaultScheme = DevelopmentAuthScheme;
|
||||
options.DefaultChallengeScheme = DevelopmentAuthScheme;
|
||||
})
|
||||
.AddPolicyScheme(DevelopmentAuthScheme, null, options =>
|
||||
{
|
||||
options.ForwardDefaultSelector = context =>
|
||||
{
|
||||
var authHeader = context.Request.Headers.Authorization.FirstOrDefault();
|
||||
return !string.IsNullOrWhiteSpace(authHeader) &&
|
||||
authHeader.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase)
|
||||
? JwtBearerDefaults.AuthenticationScheme
|
||||
: NoAuthScheme;
|
||||
};
|
||||
})
|
||||
.AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options => ConfigureJwtBearer(options, jwtSettings))
|
||||
.AddScheme<AuthenticationSchemeOptions, NoAuthHandler>(NoAuthScheme, options => { });
|
||||
return;
|
||||
}
|
||||
|
||||
var jwtSettings = configuration.GetSection("JwtSettings").Get<JwtSettings>()!;
|
||||
|
||||
services.AddSingleton(jwtSettings);
|
||||
|
||||
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
|
||||
.AddJwtBearer(options =>
|
||||
.AddJwtBearer(options => ConfigureJwtBearer(options, jwtSettings));
|
||||
}
|
||||
|
||||
private static void ConfigureJwtBearer(JwtBearerOptions options, JwtSettings jwtSettings)
|
||||
{
|
||||
options.TokenValidationParameters = new TokenValidationParameters
|
||||
{
|
||||
ValidateIssuer = true,
|
||||
ValidIssuer = jwtSettings.Issuer,
|
||||
ValidateAudience = true,
|
||||
ValidAudience = jwtSettings.Audience,
|
||||
ValidateIssuerSigningKey = true,
|
||||
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtSettings.SecretKey!)),
|
||||
ValidateLifetime = true,
|
||||
ClockSkew = TimeSpan.Zero
|
||||
};
|
||||
|
||||
options.Events = new JwtBearerEvents
|
||||
{
|
||||
OnTokenValidated = async context =>
|
||||
{
|
||||
options.TokenValidationParameters = new TokenValidationParameters
|
||||
var currentToken = GetBearerToken(context);
|
||||
if (string.IsNullOrEmpty(currentToken))
|
||||
{
|
||||
ValidateIssuer = true,
|
||||
ValidIssuer = jwtSettings.Issuer,
|
||||
ValidateAudience = true,
|
||||
ValidAudience = jwtSettings.Audience,
|
||||
ValidateIssuerSigningKey = true,
|
||||
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtSettings.SecretKey!)),
|
||||
ValidateLifetime = true,
|
||||
ClockSkew = TimeSpan.Zero
|
||||
};
|
||||
context.Fail("Invalid token");
|
||||
return;
|
||||
}
|
||||
|
||||
options.Events = new JwtBearerEvents
|
||||
var userId = context.Principal?.FindFirst(ClaimTypes.NameIdentifier)?.Value;
|
||||
if (string.IsNullOrEmpty(userId))
|
||||
{
|
||||
OnTokenValidated = async context =>
|
||||
context.Fail("Invalid token");
|
||||
return;
|
||||
}
|
||||
|
||||
var wxUserId = context.Principal?.FindFirst(JwtHelper.WxUserIdClaimType)?.Value;
|
||||
if (string.IsNullOrEmpty(wxUserId))
|
||||
{
|
||||
var adminToken = await RedisHelper.GetAsync(JwtHelper.BuildAdminTokenKey(userId));
|
||||
if (adminToken == currentToken)
|
||||
{
|
||||
var currentToken = GetBearerToken(context);
|
||||
if (string.IsNullOrEmpty(currentToken))
|
||||
{
|
||||
context.Fail("Invalid token");
|
||||
return;
|
||||
}
|
||||
|
||||
var userId = context.Principal?.FindFirst(ClaimTypes.NameIdentifier)?.Value;
|
||||
if (string.IsNullOrEmpty(userId))
|
||||
{
|
||||
context.Fail("Invalid token");
|
||||
return;
|
||||
}
|
||||
|
||||
var adminToken = await RedisHelper.GetAsync(JwtHelper.BuildAdminTokenKey(userId));
|
||||
var wxUserId = context.Principal?.FindFirst(JwtHelper.WxUserIdClaimType)?.Value;
|
||||
if (string.IsNullOrEmpty(wxUserId) && !string.IsNullOrEmpty(adminToken))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(wxUserId))
|
||||
{
|
||||
var wechatToken = await RedisHelper.GetAsync(JwtHelper.BuildWeChatTokenKey(wxUserId, userId));
|
||||
if (wechatToken == currentToken)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
context.Fail("Token expired, please login again");
|
||||
return;
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
context.Fail("Token expired, please login again");
|
||||
return;
|
||||
}
|
||||
|
||||
var wechatToken = await RedisHelper.GetAsync(JwtHelper.BuildWeChatTokenKey(wxUserId, userId));
|
||||
if (wechatToken == currentToken)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
context.Fail("Token expired, please login again");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static string? GetBearerToken(TokenValidatedContext context)
|
||||
|
||||
Reference in New Issue
Block a user