You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ASP.NET Core 6校验Spring Security生成JWT的认证方案

ASP.NET Core Web API 6 适配Spring Security HS256 JWT 跨栈认证互通方案

以下方案可直接落地部署,无需修改现有Spring Boot 2.7.0服务的任何代码,所有校验逻辑100%对齐现有Spring Security侧的JWT生成、校验规则。

前置依赖安装

  • 通过NuGet安装与.NET 6 LTS版本匹配的认证组件:Microsoft.AspNetCore.Authentication.JwtBearer(版本选择6.0.x系列,避免高版本兼容性问题)。

配置参数对齐

所有参数必须和Java侧JwtConstants配置完全一致,在appsettings.json中添加JWT配置节点:

"JwtSettings": {
  "SigningKey": "填入与Java侧JwtConstants.SIGNING_KEY完全相同的字符串",
  "ValidIssuer": "system",
  "ClockSkew": 0
}

注意:.NET侧密钥编码必须和Java侧保持一致,统一使用UTF8编码将密钥字符串转为字节数组,完全对齐Java侧new MACSigner(SIGNING_KEY.getBytes(StandardCharsets.UTF_8))的签名逻辑,避免签名校验失败。设置ClockSkew=0是为了关闭.NET默认的5分钟过期时间宽容,和Java侧过期判断逻辑完全对齐。

中间件注册(Program.cs)

直接替换Program.cs中对应部分的代码,所有逻辑已对齐Spring侧JwtAuthenticationFilter的校验流程:

using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using System.Text;

var builder = WebApplication.CreateBuilder(args);
var jwtConfig = builder.Configuration.GetSection("JwtSettings");
var signingKeyBytes = Encoding.UTF8.GetBytes(jwtConfig["SigningKey"]!);

// 注册JWT认证服务
builder.Services.AddAuthentication(options =>
{
    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
    options.TokenValidationParameters = new TokenValidationParameters
    {
        // 签名校验:对齐HS256算法+统一密钥规则
        ValidateIssuerSigningKey = true,
        IssuerSigningKey = new SymmetricSecurityKey(signingKeyBytes),
        ValidAlgorithms = new[] { SecurityAlgorithms.HmacSha256 },

        // 签发方校验:对齐Java侧固定iss=system的规则
        ValidateIssuer = true,
        ValidIssuer = jwtConfig["ValidIssuer"],

        // 关闭固定受众校验,后续自定义逻辑对齐Java侧租户有效性判断
        ValidateAudience = false,

        // 生命周期校验:对齐Java侧令牌过期判断规则
        ValidateLifetime = true,
        ClockSkew = TimeSpan.FromSeconds(int.Parse(jwtConfig["ClockSkew"]!)),

        // 关闭.NET默认的声明名重映射,保证声明Key和Java侧完全一致
        NameClaimType = JwtRegisteredClaimNames.Sub,
        RoleClaimType = "scopes"
    };

    // 令牌校验通过后的扩展逻辑:对齐Java侧的租户有效性校验
    options.Events = new JwtBearerEvents
    {
        OnTokenValidated = async context =>
        {
            // 提取aud对应当前租户ID
            var tenantId = context.Principal?.FindFirst(JwtRegisteredClaimNames.Aud)?.Value;
            if (string.IsNullOrWhiteSpace(tenantId))
            {
                context.Fail("令牌缺少合法租户标识");
                return;
            }
            // 如需接入租户服务校验,可通过DI获取服务实例:
            // var tenantService = context.HttpContext.RequestServices.GetRequiredService<ITenantService>();
            // var isValid = await tenantService.CheckTenantStatusAsync(tenantId);
            var isValid = !string.IsNullOrWhiteSpace(tenantId); // 替换为实际租户校验逻辑
            if (!isValid)
            {
                context.Fail("当前租户状态无效,拒绝访问");
            }
        }
    };
});

// 注册授权服务,对齐Java侧权限校验规则
builder.Services.AddAuthorization(options =>
{
    // 示例:对齐Java侧ROLE_ADMIN权限的访问策略
    options.AddPolicy("AdminOnly", policy => policy.RequireRole("ROLE_ADMIN"));
});

builder.Services.AddControllers();
var app = builder.Build();

// 注意中间件顺序:Authentication必须在Authorization之前注册
app.UseAuthentication();
app.UseAuthorization();

app.MapControllers();
app.Run();

说明:JwtBearer组件默认就支持从Authorization请求头提取Bearer <token>格式的令牌,和Spring侧过滤器的提取逻辑完全一致,无需额外开发令牌提取代码。

业务接口使用方式

  • 基础鉴权:在Controller或Action上标注[Authorize]特性即可完成JWT合法性校验,效果等同于Spring侧的接口鉴权注解。
  • 权限控制:通过策略或角色校验控制接口访问权限,示例:
[ApiController]
[Route("api/invoice")]
[Authorize]
public class InvoiceController : ControllerBase
{
    /// <summary>
    /// PDF发票生成接口,仅ROLE_ADMIN权限可访问
    /// </summary>
    [HttpPost("generate")]
    [Authorize(Policy = "AdminOnly")]
    public async Task<IActionResult> GenerateInvoice([FromBody] InvoiceRequest req)
    {
        // 获取当前登录用户名(对应JWT的sub声明)
        var currentUser = User.Identity?.Name;
        // 获取当前租户ID(对应JWT的aud声明)
        var tenantId = User.FindFirst(JwtRegisteredClaimNames.Aud)?.Value;
        // 获取当前用户所有权限(对应JWT的scopes声明,多权限自动拆分为同Key多Claim)
        var permissions = User.FindAll("scopes").Select(c => c.Value).ToList();

        // 此处写入PDF生成业务逻辑
        return Ok(new { CurrentUser = currentUser, TenantId = tenantId });
    }
}

常见踩坑说明

  • 不要随意修改密钥编码格式,必须使用UTF8编码转字节数组,否则会出现签名校验失败的问题。
  • 必须显式配置NameClaimType和RoleClaimType,否则User.Identity.Name无法正确获取用户名,角色权限校验也会失效。
  • 如果Java侧的aud、scopes声明是数组类型,.NET会自动将数组元素拆分为多个同名Claim,直接调用FindAll(声明Key)即可获取全量值,无需额外做JSON反序列化。
  • 客户端调用时直接沿用原有Spring服务的请求头格式,携带Authorization: Bearer <原有JWT令牌>即可访问.NET服务,不需要做任何额外适配。

内容的提问来源于stack exchange,提问作者Đỗ Như Vỹ

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.30 00:42:14