You need to enable JavaScript to run this app. Identity Server 4对接.NET 4.6.2 Web API受保护端点重定向问题
优惠活动
大模型
产品
解决方案
定价
更多

Identity Server 4对接.NET 4.6.2 Web API受保护端点重定向问题

问题描述

我尝试将Identity Server 4用于基于.NET Framework 4.6.2开发的Web API,参照公开技术社区的相关回答说明使用了IdentityServer3.Contrib.AccessTokenValidation库,但调用受保护端点时会返回402重定向。
对比验证结果:同一个access token用于调用受Identity Server 4保护的.NET Core API时不会触发重定向,仅在.NET Framework 4.6.2环境下出现该问题。

API 侧配置代码

private void ConfigureAuth(IAppBuilder app)
{
    app.Map("/api",
            apiApp =>
            {
                apiApp.UseCors(corsOptions);
                apiApp.UseIdentityServerBearerTokenAuthentication(new IdentityServerBearerTokenAuthenticationOptions
                {
                    Authority = apiAuthority,
                    ValidationMode = ValidationMode.ValidationEndpoint,
                    RequiredScopes = new[] { "api" }
                });
                SetupOpenIdAuthentication(apiApp);
            });
}

private static void SetupOpenIdAuthentication(IAppBuilder app)
{
    app.UseCookieAuthentication(new CookieAuthenticationOptions
    {
        AuthenticationType = "Cookies",
        ExpireTimeSpan = new TimeSpan(0, Configuration.SessionTimeoutInMinutes, 0),
        SlidingExpiration = true
    });
    app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
    {
        Authority = apiAuthority,
        ClientId = "clientId",
        RedirectUri = apiRootUri + "Help",
        PostLogoutRedirectUri = apiRootUri,
        ResponseType = "id_token token",
        Scope = "openid profile roles api all_claims",
        SignInAsAuthenticationType = "Cookies",
        Notifications = new OpenIdConnectAuthenticationNotifications()
        {
            RedirectToIdentityProvider = n =>
            {
                // 登出请求中补充id_token_hint参数
                if (n.ProtocolMessage.RequestType == Microsoft.IdentityModel.Protocols.OpenIdConnect.OpenIdConnectRequestType.Logout)
                {
                    var idTokenHint = n.OwinContext.Authentication.User.FindFirst("id_token");
                    if (idTokenHint != null)
                    {
                        n.ProtocolMessage.IdTokenHint = idTokenHint.Value;
                    }
                }

                if (n.ProtocolMessage.RequestType == Microsoft.IdentityModel.Protocols.OpenIdConnect.OpenIdConnectRequestType.Authentication)
                {
                    // 拦截AJAX请求的401响应,不触发默认重定向
                    if (IsAjaxRequest(n.Request) && n.Response.StatusCode == (int)System.Net.HttpStatusCode.Unauthorized)
                    {
                        n.Response.StatusCode = (int)System.Net.HttpStatusCode.Unauthorized;
                        n.HandleResponse();
                        return Task.FromResult(0);
                    }
                }

                return Task.FromResult(0);
            },
            SecurityTokenValidated = n =>
            {
                // 留存id_token用于登出流程
                n.AuthenticationTicket.Identity.AddClaim(
                    new Claim("id_token", n.ProtocolMessage.IdToken));

                // 将Role声明转换为微软标准声明类型,确保未携带Bearer Token时API的Authorize特性可正常生效
                foreach (var claim in n.AuthenticationTicket.Identity.Claims.Where(x => x.Type == JwtClaimTypes.Role).ToList())
                {
                    n.AuthenticationTicket.Identity.AddClaim(new Claim(ClaimTypes.Role, claim.Value));
                }

                return Task.FromResult(0);
            },
            AuthenticationFailed = n =>
            {
                // 认证失败时重定向到错误页面
                n.OwinContext.Response.Redirect("/Help/Error");
                // TODO: 开发专属认证失败提示页
                n.HandleResponse(); // 屏蔽默认异常抛出逻辑
                return Task.FromResult(0);
            }
        }
    });
}

Identity Server 侧配置代码

var builder = services.AddIdentityServer(options =>
{
    options.Events.RaiseErrorEvents = true;
    options.Events.RaiseInformationEvents = true;
    options.Events.RaiseFailureEvents = true;
    options.Events.RaiseSuccessEvents = true;

    // 开启静态audience声明输出
    options.EmitStaticAudienceClaim = true;
})
.AddSigningCredential(rsaCertificate)
.AddInMemoryIdentityResources(IdentityConfig.IdentityResources)
.AddInMemoryApiScopes(IdentityConfig.ApiScopes)
.AddInMemoryClients(IdentityConfig.Clients)
.AddAspNetIdentity<User>()
.AddProfileService<CustomProfileService>()
.AddWsFederationPlugin(options =>
{
    options.Licensee = "License";
    options.LicenseKey = "Key"
})
.AddInMemoryRelyingParties(new List<RelyingParty>());

services.AddAuthentication(sharedOptions =>
{
    sharedOptions.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    sharedOptions.DefaultChallengeScheme = WsFederationDefaults.AuthenticationScheme;
})
.AddWsFederation(options =>
{
    options.Wtrealm = "azureAppId";
    options.MetadataAddress = "metadataAddress";
});

builder.Services.ConfigureApplicationCookie(options =>
{
    options.Cookie.IsEssential = true;
    options.Cookie.SameSite = SameSiteMode.None; // .NET Core 3.1环境下对应SameSiteMode.Unspecified
});

Identity Server 中注册的API客户端配置

new Client
{
    ClientId = "clientId",
    AllowedGrantTypes = GrantTypes.ImplicitAndClientCredentials,
    ClientSecrets = { new Secret("secret".Sha256()) },
    RedirectUris = { "https://localhost:44302/", $"{apiUrl}/Help" },
    PostLogoutRedirectUris = { $"{apiUrl}" },
    AllowedScopes = new List<string>
    {
        IdentityServerConstants.StandardScopes.OpenId,
        IdentityServerConstants.StandardScopes.Profile,
        "api",
        "roles",
        "all_claims"
    },
    RequirePkce = false,
    AllowOfflineAccess = true,
    AllowAccessTokensViaBrowser = true
}

补充说明

如需进一步排查问题可提供更多配置代码。

内容的提问来源于stack exchange,提问作者Nimish David Mathew

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.11.09 14:54:04