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

ASP.NET Core Windows认证:如何拒绝无业务角色的已认证用户

问题:ASP.NET Core Windows认证下无业务角色时返回401未授权

我有一个部署在IIS上的内网ASP.NET Core Web应用,采用Windows认证,由ASP.NET MVC 5及WebForms应用迁移而来。

该应用通过实现IClaimsTransformer来确定用户的特定业务角色,角色以role类型的声明添加。若用户无任何业务角色,则无法访问应用。

现在需要实现:当用户无业务角色时返回401-未授权,且不触发授权检查点。旧应用通过RoleProvider类实现了类似效果——用户无角色时会重复提示输入密码,这是旧框架内置行为。但当前.NET Core版本中,应用触发授权检查点后会返回403,不符合需求。

以下是Program.cs中的代码:

using log4net.Config;
using System.Reflection;
using log4net;
using Microsoft.AspNetCore.Authentication.Negotiate;
using Newtonsoft.Json.Serialization;
using Newtonsoft.Json;
using Microsoft.AspNetCore.Authorization;


using MicroKnights.Log4NetHelper;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Server.IIS;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Diagnostics;


var builder = WebApplication.CreateBuilder(args);

// AddLog4Net comes from Microsoft.Extensions.Logging.Log4Net.AspNetCore
builder.Logging.AddLog4Net("log4net.config", true);

var log = LogManager.GetLogger(Assembly.GetEntryAssembly().GetType());

// Add services to the container.
builder.Services.AddControllersWithViews().AddMvcOptions(
    options =>
    {
    })
  .AddRazorRuntimeCompilation()
  .AddNewtonsoftJson(
    options =>
    {
    });;

builder.Services.AddAuthentication(NegotiateDefaults.AuthenticationScheme)
  .AddNegotiate()
  ;

builder.Services.AddTransient<IClaimsTransformation, AppClaimsTransformer>();
builder.Services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();

builder.Services.AddDistributedMemoryCache();
builder.Services.AddSession(options =>
{
  options.IdleTimeout = TimeSpan.FromMinutes(20);
  options.IOTimeout = Timeout.InfiniteTimeSpan;
  options.Cookie.HttpOnly = true;
  options.Cookie.IsEssential = true;
});

builder.Services.AddAuthorization(options =>
{

  options.FallbackPolicy = //options.DefaultPolicy;    
    new AuthorizationPolicyBuilder()
      .RequireAuthenticatedUser()
      .RequireRole(RolesConstants.DefaultRole)
      .Build();
});

var app = builder.Build();

if (app.Environment.IsDevelopment())
{
  app.UseDeveloperExceptionPage();
}
else 
{
  app.UseExceptionHandler("/Home/Error");
  app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStatusCodePagesWithReExecute("/home/status/{0}");

app.UseRouting();
app.UseSession();

app.UseAuthentication();
app.UseAuthorization();

app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}");

app.Run();

解决方案

1. 修改IClaimsTransformer实现,无角色时抛出认证异常

在AppClaimsTransformer的TransformAsync方法中,检测到用户无业务角色时直接抛出异常,在认证流程早期拦截:

public class AppClaimsTransformer : IClaimsTransformation
{
    private readonly IHttpContextAccessor _httpContextAccessor;

    public AppClaimsTransformer(IHttpContextAccessor httpContextAccessor)
    {
        _httpContextAccessor = httpContextAccessor;
    }

    public async Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal principal)
    {
        // 替换为你的业务角色获取逻辑
        var userRoles = await GetUserBusinessRoles(principal.Identity.Name);

        if (!userRoles.Any())
        {
            // 无角色时抛出异常,避免进入后续授权环节
            throw new UnauthorizedAccessException("用户无业务角色,无法访问应用");
        }

        var claimsIdentity = new ClaimsIdentity(principal.Identity);
        foreach (var role in userRoles)
        {
            claimsIdentity.AddClaim(new Claim(ClaimTypes.Role, role));
        }

        return new ClaimsPrincipal(claimsIdentity);
    }

    private Task<List<string>> GetUserBusinessRoles(string userName)
    {
        // 这里实现从数据库/权限系统获取用户角色的逻辑
        return Task.FromResult(new List<string>());
    }
}

2. 添加全局异常处理中间件,返回401并触发认证提示

在Program.cs中,在UseAuthentication之前添加自定义中间件,捕获无角色异常并返回401,同时设置响应头触发Windows认证弹窗:

var app = builder.Build();

// ... 环境配置代码(如开发/生产环境处理) ...

// 自定义异常处理中间件,放在认证中间件之前
app.Use(async (context, next) =>
{
    try
    {
        await next();
    }
    catch (UnauthorizedAccessException ex)
    {
        context.Response.StatusCode = StatusCodes.Status401Unauthorized;
        // 设置WWW-Authenticate头,触发Windows认证重新提示
        context.Response.Headers.Append("WWW-Authenticate", "Negotiate");
        await context.Response.WriteAsync(ex.Message);
    }
});

app.UseHttpsRedirection();
app.UseStatusCodePagesWithReExecute("/home/status/{0}");

app.UseRouting();
app.UseSession();

app.UseAuthentication();
app.UseAuthorization();

// ... 路由配置 ...

3. 简化授权策略(可选)

由于已经在认证流程早期拦截无角色用户,可以简化授权策略,只要求用户已认证即可:

builder.Services.AddAuthorization(options =>
{
    options.FallbackPolicy = new AuthorizationPolicyBuilder()
        .RequireAuthenticatedUser()
        .Build();
});

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 15:53:13