C# 使用IdentityServer4进行身份验证时重定向错误

C# 使用IdentityServer4进行身份验证时重定向错误,c#,asp.net-core,asp.net-core-mvc,identityserver4,asp.net-core-3.1,C#,Asp.net Core,Asp.net Core Mvc,Identityserver4,Asp.net Core 3.1,在客户端进行身份验证后,我得到错误: Microsoft.AspNetCore.Authentication.RemoteAuthenticationHandler 消息包含错误:“”,错误描述:“错误描述为” null',error\u uri:'error\u uri为null' 失败: Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectHandler[52] 消息包含错误:'(null)',错误描述:'错误描述为n

在客户端进行身份验证后,我得到错误:

Microsoft.AspNetCore.Authentication.RemoteAuthenticationHandler 消息包含错误:“”,错误描述:“错误描述为” null',error\u uri:'error\u uri为null'

失败: Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectHandler[52] 消息包含错误:'(null)',错误描述:'错误描述为null',错误uri:'错误uri为null',状态 代码“500”。失败: Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectHandler[17] 处理消息时发生异常。Microsoft.IdentityModel.Protocols.OpenIdConnect.OpenIdConnectProtocolException: 消息包含错误:“”,错误描述:“错误描述为” null',error\u uri:'error\u uri为null'。在 Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectHandler.ReceiveAuthorizationCodeAsync(OpenIdConnectMessage 令牌(请求)位于 Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectHandler.HandlerEmoteAuthenticationAsync() 失败:eCleverShopSolution.WebApp.Helpers.ErrorWrappingMiddleware[0] 处理远程登录时遇到错误。System.Exception:处理远程服务器时遇到错误 登录。--> Microsoft.IdentityModel.Protocols.OpenIdConnect.OpenIdConnectProtocolException: 消息包含错误:“”,错误描述:“错误描述为” null',error\u uri:'error\u uri为null'。在 Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectHandler.ReceiveAuthorizationCodeAsync(OpenIdConnectMessage 令牌(请求)位于 Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectHandler.HandlerEmoteAuthenticationAsync() ---内部异常堆栈跟踪结束---位于Microsoft.AspNetCore.Authentication.RemoteAuthenticationHandler`1.HandlerRequestAsync() 在 Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext (上下文)在 eCleverShopSolution.WebApp.Helpers.ErrorWrappingMiddleware.Invoke(HttpContext 上下文)在D:\CODE\Web中 开发者\ASP.NET\eCleverShopSolution\src\eCleverShopSolution.WebApp\Helpers\ErrorWrappingMiddleware.cs:line 二十三

以下是客户端项目中的代码:

    public void ConfigureServices(IServiceCollection services)
    {     
        services.AddHttpClient("BackendApi").ConfigurePrimaryHttpMessageHandler(() =>
        {
            var handler = new HttpClientHandler();
            var environment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");

            //if (environment == Environments.Development)
            //{
            //    handler.ServerCertificateCustomValidationCallback = (message, cert, chain, errors) => { return true; };
            //}
            handler.ServerCertificateCustomValidationCallback = (message, cert, chain, errors) => { return true; };
            return handler;
        });
        services.AddSession(options =>
        {
            options.IdleTimeout = TimeSpan.FromMinutes(30);
            options.Cookie.HttpOnly = true;
        });
        services.AddAuthentication(options =>
        {
            options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
            options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
        })
            .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options =>
            {
                options.Events = new CookieAuthenticationEvents
                {
                    // this event is fired everytime the cookie has been validated by the cookie middleware,
                    // so basically during every authenticated request
                    // the decryption of the cookie has already happened so we have access to the user claims
                    // and cookie properties - expiration, etc..
                    OnValidatePrincipal = async x =>
                     {
                        // since our cookie lifetime is based on the access token one,
                        // check if we're more than halfway of the cookie lifetime
                        var now = DateTimeOffset.UtcNow;
                         var timeElapsed = now.Subtract(x.Properties.IssuedUtc.Value);
                         var timeRemaining = x.Properties.ExpiresUtc.Value.Subtract(now);

                         if (timeElapsed > timeRemaining)
                         {
                             var identity = (ClaimsIdentity)x.Principal.Identity;
                             var accessTokenClaim = identity.FindFirst("access_token");
                             var refreshTokenClaim = identity.FindFirst("refresh_token");

                            // if we have to refresh, grab the refresh token from the claims, and request
                            // new access token and refresh token
                            var refreshToken = refreshTokenClaim.Value;
                             var response = await new HttpClient().RequestRefreshTokenAsync(new RefreshTokenRequest
                             {
                                 Address = Configuration["Authorization:AuthorityUrl"],
                                 ClientId = Configuration["Authorization:ClientId"],
                                 ClientSecret = Configuration["Authorization:ClientSecret"],
                                 RefreshToken = refreshToken
                             });

                             if (!response.IsError)
                             {
                                // everything went right, remove old tokens and add new ones
                                identity.RemoveClaim(accessTokenClaim);
                                 identity.RemoveClaim(refreshTokenClaim);

                                 identity.AddClaims(new[]
                                 {
                                    new Claim("access_token", response.AccessToken),
                                    new Claim("refresh_token", response.RefreshToken)
                                  });

                                // indicate to the cookie middleware to renew the session cookie
                                // the new lifetime will be the same as the old one, so the alignment
                                // between cookie and access token is preserved
                                x.ShouldRenew = true;
                             }
                         }
                     }
                };
            })
             .AddOpenIdConnect("oidc", options =>
             {
                 options.Authority = Configuration["Authorization:AuthorityUrl"];
                 options.RequireHttpsMetadata = false;
                 options.GetClaimsFromUserInfoEndpoint = true;

                 options.ClientId = Configuration["Authorization:ClientId"];
                 options.ClientSecret = Configuration["Authorization:ClientSecret"];
                 options.ResponseType = "code";

                 options.SaveTokens = true;

                 options.Scope.Add("openid");
                 options.Scope.Add("profile");
                 options.Scope.Add("offline_access");
                 options.Scope.Add("api.eclevershop");

                 options.TokenValidationParameters = new TokenValidationParameters
                 {
                     NameClaimType = "name",
                     RoleClaimType = "role"
                 };
                 options.Events = new OpenIdConnectEvents
                 {
                     // that event is called after the OIDC middleware received the auhorisation code,
                     // redeemed it for an access token and a refresh token,
                     // and validated the identity token
                     OnTokenValidated = x =>
                      {
                         // store both access and refresh token in the claims - hence in the cookie
                         var identity = (ClaimsIdentity)x.Principal.Identity;
                          identity.AddClaims(new[]
                          {
                            new Claim("access_token", x.TokenEndpointResponse.AccessToken),
                            new Claim("refresh_token", x.TokenEndpointResponse.RefreshToken)
                          });

                         // so that we don't issue a session cookie but one with a fixed expiration
                         x.Properties.IsPersistent = true;

                         // align expiration of the cookie with expiration of the
                         // access token
                         var accessToken = new JwtSecurityToken(x.TokenEndpointResponse.AccessToken);
                          x.Properties.ExpiresUtc = accessToken.ValidTo;

                          return Task.CompletedTask;
                      }
                 };
             });

        var builder = services.AddControllersWithViews();

        var environment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
        if (environment == Environments.Development)
        {
            builder.AddRazorRuntimeCompilation();
        }

        //Declare DI containers
        services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();

    }
public void配置服务(IServiceCollection服务)
{     
services.AddHttpClient(“后端API”).ConfigurePrimaryHttpMessageHandler(()=>
{
var handler=new-HttpClientHandler();
var-environment=environment.GetEnvironmentVariable(“ASPNETCORE_-environment”);
//if(environment==Environments.Development)
//{
//handler.ServerCertificateCostomValidationCallback=(消息、证书、链、错误)=>{return true;};
//}
handler.ServerCertificateCostomValidationCallback=(消息、证书、链、错误)=>{return true;};
返回处理程序;
});
services.AddSession(选项=>
{
options.IdleTimeout=TimeSpan.FromMinutes(30);
options.Cookie.HttpOnly=true;
});
services.AddAuthentication(选项=>
{
options.DefaultScheme=CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme=OpenIdConnectDefaults.AuthenticationScheme;
})
.AddCookie(CookieAuthenticationDefaults.AuthenticationScheme,选项=>
{
options.Events=新建CookieAuthenticationEvents
{
//每次cookie中间件验证cookie时都会触发此事件,
//所以基本上在每个经过身份验证的请求中
//cookie的解密已经发生,因此我们可以访问用户声明
//和cookie属性-过期等。。
OnValidatePrincipal=异步x=>
{
//因为我们的cookie生存期是基于访问令牌1的,
//检查我们是否超过了饼干寿命的一半
var now=DateTimeOffset.UtcNow;
var timeappeased=now.Subtract(x.Properties.IssuedUtc.Value);
var timeRemaining=x.Properties.ExpiresUtc.Value.Subtract(现在);
如果(时间已过>剩余时间)
{
var identity=(ClaimsIdentity)x.Principal.identity;
var accessTokenClaim=identity.FindFirst(“访问令牌”);
var refreshttokenclaim=identity.FindFirst(“刷新令牌”);
//如果我们必须刷新,从声明中获取刷新令牌,然后请求
//新的访问令牌和刷新令牌
var refreshtToken=refreshtTokenClaim.Value;
var response=wait new HttpClient().RequestRefreshTokenAsync(new RefreshTokenRequest
{
地址=配置[“授权:授权URL”],
ClientId=配置[“授权:ClientId”],
ClientSecret=配置[“授权:ClientSecret”],
RefreshToken=RefreshToken
});
如果(!response.IsError)
{
//一切正常,移除旧代币并添加新代币
identity.RemoveClaim(accessTokenClaim);
标识。RemoveClaim(refreshTokenClaim);
identity.AddClaims(新[]
{
新声明(“访问令牌”,response.AccessToken),
新索赔(“刷新令牌”,分别为
public class Config
{
    public static IEnumerable<IdentityResource> Ids =>
      new IdentityResource[]
      {
            new IdentityResources.OpenId(),
            new IdentityResources.Profile()
      };

    public static IEnumerable<ApiResource> Apis =>
        new ApiResource[]
        {
            new ApiResource("api.eclevershop", "EClever Shop API")
        };

    public static IEnumerable<Client> Clients =>
        new Client[]
        {
            new Client
            {
                ClientId = "webportal",
                ClientSecrets = { new Secret("secret".Sha256()) },

                AllowedGrantTypes = GrantTypes.Code,
                RequireConsent = false,
                RequirePkce = true,
                AllowOfflineAccess = true,

                // where to redirect to after login
                RedirectUris = { "https://localhost:5002/signin-oidc" },

                // where to redirect to after logout
                PostLogoutRedirectUris = { "https://localhost:5002/signout-callback-oidc" },

                AllowedScopes = new List<string>
                {
                    IdentityServerConstants.StandardScopes.OpenId,
                    IdentityServerConstants.StandardScopes.Profile,
                    IdentityServerConstants.StandardScopes.OfflineAccess,
                    "api.eclevershop"
                }
             },
        }
   }
public static IEnumerable<ApiResource> Apis =>
    new ApiResource[]
    {
        new ApiResource("api.eclevershop", "EClever Shop API")
    };