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

.NET Core 3.1项目如何为内外部用户适配Windows与Forms认证

内网自动Windows认证、外网跳转Forms登录的实现方案

要实现你需要的逻辑——内网/域环境自动走Windows认证进入仪表盘,域外访问自动跳转Forms登录页,可以按以下步骤操作,适配IIS部署的ASP.NET(Framework/Core)应用:

一、先搞定IIS的认证配置

  • 打开IIS站点的身份验证模块,同时启用Windows身份验证和表单身份验证,必须保留匿名认证启用状态——因为得先检测请求来源,再决定用哪种认证,要是关了匿名,会直接强制Windows认证,没法做切换。
  • 确保应用程序池的标识有足够权限访问域内资源,不然Windows认证会失败。

二、编写内网/域环境的检测逻辑

核心是判断请求是否来自内网,有两种常用方式,按需选择:

方式1:按IP地址范围判断

把你的内网私有IP段列出来,匹配客户端IP:

// ASP.NET MVC示例,可放在过滤器或控制器中
private bool IsInternalRequest(HttpRequestBase request)
{
    string clientIp = request.UserHostAddress;
    // IP段根据你的实际内网环境调整
    var internalIpPrefixes = new List<string> 
    { 
        "192.168.", "10.", 
        "172.16.", "172.17.", "172.18.", "172.19.", 
        "172.20.", "172.21.", "172.22.", "172.23.", 
        "172.24.", "172.25.", "172.26.", "172.27.", 
        "172.28.", "172.29.", "172.30.", "172.31." 
    };
    return internalIpPrefixes.Any(prefix => clientIp.StartsWith(prefix));
}

方式2:直接检测域用户

如果内网设备都加入了域,可以尝试触发Windows认证后,判断当前用户是否为域用户:

private bool IsDomainAuthenticatedUser()
{
    var windowsIdentity = HttpContext.Current.User.Identity as WindowsIdentity;
    return windowsIdentity != null && windowsIdentity.IsAuthenticated 
           && !windowsIdentity.IsAnonymous && windowsIdentity.Name.Contains('\\');
}

三、用全局过滤器/中间件处理自动跳转

根据你的ASP.NET版本选择对应实现:

ASP.NET Framework(MVC):全局ActionFilter

编写过滤器,在请求进入时做判断处理:

public class AuthSwitchFilter : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        var httpContext = filterContext.HttpContext;
        var request = httpContext.Request;
        var response = httpContext.Response;

        // 已认证用户直接放行
        if (httpContext.User.Identity.IsAuthenticated)
        {
            base.OnActionExecuting(filterContext);
            return;
        }

        if (IsInternalRequest(request))
        {
            // 内网请求:触发Windows认证
            response.StatusCode = 401;
            response.AddHeader("WWW-Authenticate", "Negotiate");
            response.AddHeader("WWW-Authenticate", "NTLM");
            response.End();
        }
        else
        {
            // 外网请求:跳转到Forms登录页
            filterContext.Result = new RedirectResult("~/Account/Login");
        }

        base.OnActionExecuting(filterContext);
    }

    private bool IsInternalRequest(HttpRequestBase request)
    {
        // 复用前面的IP检测逻辑
        string clientIp = request.UserHostAddress;
        var internalIpPrefixes = new List<string> 
        { 
            "192.168.", "10.", 
            "172.16.", "172.17.", "172.18.", "172.19.", 
            "172.20.", "172.21.", "172.22.", "172.23.", 
            "172.24.", "172.25.", "172.26.", "172.27.", 
            "172.28.", "172.29.", "172.30.", "172.31." 
        };
        return internalIpPrefixes.Any(prefix => clientIp.StartsWith(prefix));
    }
}

然后在Global.asax.cs的Application_Start中注册过滤器:

protected void Application_Start()
{
    // 其他初始化代码...
    GlobalFilters.Filters.Add(new AuthSwitchFilter());
}

ASP.NET Core:自定义中间件

在Program.cs中添加中间件,放在认证中间件之前:

// 先注册认证服务
builder.Services.AddAuthentication(options =>
{
    options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
})
.AddNegotiate() // 启用Windows认证
.AddCookie(options => // 配置Forms认证登录页
{
    options.LoginPath = "/Account/Login";
});

// 添加自定义中间件处理认证切换
app.Use(async (context, next) =>
{
    if (!context.User.Identity.IsAuthenticated)
    {
        var clientIp = context.Connection.RemoteIpAddress.ToString();
        // 判断是否为内网IP
        bool isInternal = clientIp.StartsWith("192.168.") || clientIp.StartsWith("10.") 
                          || clientIp.StartsWith("172.16.") || clientIp.StartsWith("172.17.") 
                          || clientIp.StartsWith("172.18.") || clientIp.StartsWith("172.19.") 
                          || clientIp.StartsWith("172.20.") || clientIp.StartsWith("172.21.") 
                          || clientIp.StartsWith("172.22.") || clientIp.StartsWith("172.23.") 
                          || clientIp.StartsWith("172.24.") || clientIp.StartsWith("172.25.") 
                          || clientIp.StartsWith("172.26.") || clientIp.StartsWith("172.27.") 
                          || clientIp.StartsWith("172.28.") || clientIp.StartsWith("172.29.") 
                          || clientIp.StartsWith("172.30.") || clientIp.StartsWith("172.31.");

        if (isInternal)
        {
            // 内网请求:触发Windows认证
            await context.ChallengeAsync(NegotiateDefaults.AuthenticationScheme);
            return;
        }
        else
        {
            // 外网请求:跳转到Forms登录页
            context.Response.Redirect("/Account/Login");
            return;
        }
    }
    // 已认证用户继续执行后续逻辑
    await next();
});

// 启用认证中间件
app.UseAuthentication();
app.UseAuthorization();

四、ASP.NET Framework的web.config补充配置

确保web.config中同时启用两种认证,并允许匿名访问:

<system.web>
  <authentication mode="Forms">
    <forms loginUrl="~/Account/Login" timeout="2880" />
  </authentication>
  <authorization>
    <allow users="?" /> <!-- 允许匿名访问,才能先做来源检测 -->
  </authorization>
</system.web>
<system.webServer>
  <security>
    <authentication>
      <anonymousAuthentication enabled="true" />
      <windowsAuthentication enabled="true" />
      <formsAuthentication enabled="true" loginUrl="~/Account/Login" />
    </authentication>
  </security>
</system.webServer>

五、测试要点

  • 内网测试:用域内设备访问,浏览器应自动完成Windows认证(或用已保存凭据登录),成功后跳转至仪表盘。
  • 外网测试:用非域内设备或公网IP访问,确认自动跳转到Forms登录页。
  • 若内网有反向代理,需确保IIS能获取真实客户端IP(可在IIS中启用X-Forwarded-For头解析,避免把代理IP当成客户端IP)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 17:50:43