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

ASP.NET 6 添加授权IMiddleware后出现CORS问题求助

自定义授权中间件导致CORS配置失效的排查与解决

问题场景

我在负责的项目中新增了自定义角色创建系统,允许用户自行创建角色。为此实现了一个AuthorizationMiddleware(实现IMiddleware)来拦截所有客户端请求,验证请求的API路径是否包含在用户角色授权的操作范围内。该中间件会调用数据库获取用户信息并进行校验,若校验不通过则抛出UnauthorizedAccessException,否则继续执行后续逻辑。

项目中同时使用Fluent Validation,它也会频繁调用同一数据库,但未出现问题,因此排除数据库原因。目前不清楚该中间件为何会影响CORS配置,已查阅相关资料但未找到解决办法。

以下是中间件实现代码:

public class AuthorizationMiddleware : IMiddleware
{
    readonly IQueryById Query;

    public AuthorizationMiddleware(IQueryById query)
    {
        Query = query;
    }

    public async Task InvokeAsync(HttpContext context, RequestDelegate next)
    {
        string relativePath = context.Request.Path.Value.Substring(1);
        if (GetWhiteListApiUrls().Contains(relativePath))
        {
            await next.Invoke(context).ConfigureAwait(false);
            return;
        }
        string requestType = context.Request.Method;
        string protocol = $"{requestType}_{relativePath}";

        var userId = GetUserIdFromHttpContext(context);
        var user = await Query.GetById<User>(userId);
        var roles = await Query.GetByIds<Role>(user.Roles.Select(x => x.Id));
        var actions = roles.SelectMany(x => x.Actions);

        if (!actions.Select(x => x.RelativePathProtocol).Contains(protocol))
            throw new UnauthorizedAccessException();

        await next.Invoke(context).ConfigureAwait(false);
    }

    static string GetUserIdFromHttpContext(HttpContext context)
    {
        string jwt = context.Request.Headers["Authorization"].ToString().Replace("Bearer ", "");
        var handler = new JwtSecurityTokenHandler();
        var token = handler.ReadToken(jwt) as JwtSecurityToken;
        string userId = token.Subject.ToString();
        return Common.IdUtils.GetUserId(userId);
    }
}

问题原因分析

  1. 中间件执行顺序错误:CORS中间件需要在授权类中间件之前执行。如果AuthorizationMiddleware注册在UseCors之前,会导致预检请求(OPTIONS)被提前拦截,此时CORS响应头还未设置,浏览器会判定为跨域错误。
  2. 预检请求未被放行:跨域请求前浏览器会发送OPTIONS预检请求,该请求不会携带Authorization头。你的GetUserIdFromHttpContext方法未处理这种情况,会抛出空引用或授权异常,导致请求失败且CORS头缺失。
  3. 异常处理未携带CORS头:抛出UnauthorizedAccessException后,默认异常处理流程不会自动添加CORS响应头,浏览器收到错误响应但缺少跨域所需的头信息,从而触发跨域拦截。

解决方案

1. 调整中间件注册顺序

在Program.cs(或Startup.cs)中确保CORS中间件优先于授权中间件注册:

// 先注册CORS中间件
app.UseCors(builder =>
{
    builder.WithOrigins("你的允许跨域地址")
           .AllowAnyHeader()
           .AllowAnyMethod();
});

// 再注册自定义授权中间件
app.UseMiddleware<AuthorizationMiddleware>();

2. 放行OPTIONS预检请求

修改中间件逻辑,优先处理OPTIONS请求,不进行授权校验:

public async Task InvokeAsync(HttpContext context, RequestDelegate next)
{
    // 优先处理OPTIONS预检请求
    if (context.Request.Method.Equals("OPTIONS", StringComparison.OrdinalIgnoreCase))
    {
        await next.Invoke(context).ConfigureAwait(false);
        return;
    }

    // 原有逻辑...
    string relativePath = context.Request.Path.Value.Substring(1);
    if (GetWhiteListApiUrls().Contains(relativePath))
    {
        await next.Invoke(context).ConfigureAwait(false);
        return;
    }
    // ...剩余授权逻辑
}

3. 安全处理Authorization头

在GetUserIdFromHttpContext中增加空值校验,避免空引用异常:

static string GetUserIdFromHttpContext(HttpContext context)
{
    if (!context.Request.Headers.TryGetValue("Authorization", out var authHeader) || string.IsNullOrWhiteSpace(authHeader))
    {
        throw new UnauthorizedAccessException();
    }

    string jwt = authHeader.ToString().Replace("Bearer ", "");
    if (string.IsNullOrWhiteSpace(jwt) || !new JwtSecurityTokenHandler().CanReadToken(jwt))
    {
        throw new UnauthorizedAccessException();
    }

    var token = new JwtSecurityTokenHandler().ReadToken(jwt) as JwtSecurityToken;
    string userId = token?.Subject ?? throw new UnauthorizedAccessException();
    
    return Common.IdUtils.GetUserId(userId);
}

4. 异常处理时保留CORS头

自定义异常处理中间件,确保异常响应也包含CORS头:

public class ExceptionHandlerMiddleware : IMiddleware
{
    public async Task InvokeAsync(HttpContext context, RequestDelegate next)
    {
        try
        {
            await next(context);
        }
        catch (Exception ex)
        {
            // 确保CORS头已设置
            if (!context.Response.HasStarted)
            {
                // 可根据实际CORS配置添加相应头,或复用已配置的CORS逻辑
                context.Response.Headers.Append("Access-Control-Allow-Origin", "你的允许跨域地址");
            }

            context.Response.StatusCode = StatusCodes.Status401Unauthorized;
            await context.Response.WriteAsync(ex.Message);
        }
    }
}

注册该异常中间件时需放在CORS和授权中间件之后。

内容的提问来源于stack exchange,提问作者Mladen Ristic

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 11:01:34