feat: add JWT authentication to gateway
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
parent
0cbb5a2c0e
commit
2a4a06ddb8
@ -1,3 +1,4 @@
|
|||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using YarpGateway.Data;
|
using YarpGateway.Data;
|
||||||
@ -9,6 +10,7 @@ namespace YarpGateway.Controllers;
|
|||||||
|
|
||||||
[ApiController]
|
[ApiController]
|
||||||
[Route("api/gateway")]
|
[Route("api/gateway")]
|
||||||
|
[Authorize] // 要求所有管理 API 都需要认证
|
||||||
public class GatewayConfigController : ControllerBase
|
public class GatewayConfigController : ControllerBase
|
||||||
{
|
{
|
||||||
private readonly IDbContextFactory<GatewayDbContext> _dbContextFactory;
|
private readonly IDbContextFactory<GatewayDbContext> _dbContextFactory;
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using YarpGateway.Data;
|
using YarpGateway.Data;
|
||||||
@ -7,6 +8,7 @@ namespace YarpGateway.Controllers;
|
|||||||
|
|
||||||
[ApiController]
|
[ApiController]
|
||||||
[Route("api/gateway/pending-services")]
|
[Route("api/gateway/pending-services")]
|
||||||
|
[Authorize] // 要求所有管理 API 都需要认证
|
||||||
public class PendingServicesController : ControllerBase
|
public class PendingServicesController : ControllerBase
|
||||||
{
|
{
|
||||||
private readonly IDbContextFactory<GatewayDbContext> _dbContextFactory;
|
private readonly IDbContextFactory<GatewayDbContext> _dbContextFactory;
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
using Serilog;
|
using Serilog;
|
||||||
@ -23,10 +24,48 @@ builder.Host.UseSerilog(
|
|||||||
.Enrich.FromLogContext()
|
.Enrich.FromLogContext()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// 配置 JWT
|
||||||
builder.Services.Configure<JwtConfig>(builder.Configuration.GetSection("Jwt"));
|
builder.Services.Configure<JwtConfig>(builder.Configuration.GetSection("Jwt"));
|
||||||
builder.Services.Configure<RedisConfig>(builder.Configuration.GetSection("Redis"));
|
builder.Services.Configure<RedisConfig>(builder.Configuration.GetSection("Redis"));
|
||||||
builder.Services.AddSingleton(sp => sp.GetRequiredService<IOptions<RedisConfig>>().Value);
|
builder.Services.AddSingleton(sp => sp.GetRequiredService<IOptions<RedisConfig>>().Value);
|
||||||
|
|
||||||
|
// 添加 JWT 认证
|
||||||
|
var jwtConfig = builder.Configuration.GetSection("Jwt").Get<JwtConfig>();
|
||||||
|
if (jwtConfig != null && !string.IsNullOrEmpty(jwtConfig.Authority))
|
||||||
|
{
|
||||||
|
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
|
||||||
|
.AddJwtBearer(options =>
|
||||||
|
{
|
||||||
|
options.Authority = jwtConfig.Authority;
|
||||||
|
options.Audience = jwtConfig.Audience;
|
||||||
|
options.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
|
||||||
|
{
|
||||||
|
ValidateIssuer = jwtConfig.ValidateIssuer,
|
||||||
|
ValidateAudience = jwtConfig.ValidateAudience,
|
||||||
|
ValidateLifetime = true,
|
||||||
|
ValidateIssuerSigningKey = true,
|
||||||
|
ClockSkew = TimeSpan.Zero
|
||||||
|
};
|
||||||
|
|
||||||
|
options.Events = new JwtBearerEvents
|
||||||
|
{
|
||||||
|
OnAuthenticationFailed = context =>
|
||||||
|
{
|
||||||
|
Log.Warning("JWT authentication failed: {Error}", context.Exception?.Message);
|
||||||
|
return Task.CompletedTask;
|
||||||
|
},
|
||||||
|
OnTokenValidated = context =>
|
||||||
|
{
|
||||||
|
Log.Information("JWT token validated for user: {User}",
|
||||||
|
context.Principal?.FindFirst(System.Security.Claims.ClaimTypes.NameIdentifier)?.Value ?? "unknown");
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
builder.Services.AddAuthorization();
|
||||||
|
}
|
||||||
|
|
||||||
builder.Services.AddDbContextFactory<GatewayDbContext>(options =>
|
builder.Services.AddDbContextFactory<GatewayDbContext>(options =>
|
||||||
options.UseNpgsql(builder.Configuration.GetConnectionString("DefaultConnection"))
|
options.UseNpgsql(builder.Configuration.GetConnectionString("DefaultConnection"))
|
||||||
);
|
);
|
||||||
@ -78,6 +117,7 @@ builder.Services.AddServiceDiscovery();
|
|||||||
|
|
||||||
builder.Services.AddHostedService<KubernetesPendingSyncService>();
|
builder.Services.AddHostedService<KubernetesPendingSyncService>();
|
||||||
|
|
||||||
|
// CORS 配置 - 修复 AllowAnyOrigin 与 AllowCredentials 不兼容问题
|
||||||
var corsSettings = builder.Configuration.GetSection("Cors");
|
var corsSettings = builder.Configuration.GetSection("Cors");
|
||||||
builder.Services.AddCors(options =>
|
builder.Services.AddCors(options =>
|
||||||
{
|
{
|
||||||
@ -88,16 +128,18 @@ builder.Services.AddCors(options =>
|
|||||||
{
|
{
|
||||||
if (allowAnyOrigin)
|
if (allowAnyOrigin)
|
||||||
{
|
{
|
||||||
policy.AllowAnyOrigin();
|
// AllowAnyOrigin 与 AllowCredentials 不兼容
|
||||||
|
policy.AllowAnyOrigin()
|
||||||
|
.AllowAnyHeader()
|
||||||
|
.AllowAnyMethod();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
policy.WithOrigins(allowedOrigins);
|
policy.WithOrigins(allowedOrigins)
|
||||||
|
.AllowAnyHeader()
|
||||||
|
.AllowAnyMethod()
|
||||||
|
.AllowCredentials();
|
||||||
}
|
}
|
||||||
|
|
||||||
policy.AllowAnyHeader()
|
|
||||||
.AllowAnyMethod()
|
|
||||||
.AllowCredentials();
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -109,6 +151,14 @@ builder.Services.AddReverseProxy();
|
|||||||
var app = builder.Build();
|
var app = builder.Build();
|
||||||
|
|
||||||
app.UseCors("AllowFrontend");
|
app.UseCors("AllowFrontend");
|
||||||
|
|
||||||
|
// 添加认证和授权中间件(必须在自定义中间件之前)
|
||||||
|
if (jwtConfig != null && !string.IsNullOrEmpty(jwtConfig.Authority))
|
||||||
|
{
|
||||||
|
app.UseAuthentication();
|
||||||
|
app.UseAuthorization();
|
||||||
|
}
|
||||||
|
|
||||||
app.UseMiddleware<JwtTransformMiddleware>();
|
app.UseMiddleware<JwtTransformMiddleware>();
|
||||||
app.UseMiddleware<TenantRoutingMiddleware>();
|
app.UseMiddleware<TenantRoutingMiddleware>();
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user