将项目从 .NET Core 3.1 迁移到 .NET 8.0 后,我收到 http 401 未经授权

问题描述 投票:0回答:1

我将项目从 .NET Core 3.1 迁移到 .NET 8,并将 Ocelot 库迁移到最新版本。

在迁移之前,项目身份验证功能运行良好。将项目迁移到 .NET 8.0 后,出现错误

未经授权401

在此网址上:

https://http://localhost:3000/swaggerfiles.json

这是我的

Startup.cs
课程:

using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Text.RegularExpressions;
using Constellation.APIGateway.Authentication.Http;
using Constellation.APIGateway.Handlers;
using Constellation.APIGateway.Swagger;
using Constellation.Authentication.Entities;
using Constellation.Common.Bus.Events;
using Constellation.Common.Exceptions;
using Constellation.Common.Kafka;
using Constellation.Common.Swagger;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Rewrite;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Serialization;
using NSwag.AspNetCore;
using Ocelot.DependencyInjection;
using Ocelot.Middleware;
using Ocelot.Provider.Polly;

namespace Constellation.APIGateway
{
    public class Startup
    {
        private readonly IConfiguration configuration;
        private readonly ILoggerFactory loggerFactory;

        public Startup(IConfiguration configuration, ILoggerFactory loggerFactory)
        {
            this.configuration = configuration;
            this.loggerFactory = loggerFactory;
        }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddCors(options =>
            {
                options.AddPolicy("AllowAll",
                    builder =>
                    {
                        builder
                        .AllowAnyOrigin()
                        .AllowAnyMethod()
                        .AllowAnyHeader()
                        .WithExposedHeaders("content-range")
                        .WithExposedHeaders("content-length")
                        .WithExposedHeaders("date")
                        .WithExposedHeaders("server")
                        .WithExposedHeaders("status")
                        .WithExposedHeaders("strict-transport-security")
                        .WithExposedHeaders("partition-offsets");
                    });
            });

            services.AddControllers().AddNewtonsoftJson(options => options.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver());
            services.AddKafka(configuration, loggerFactory);
            services.AddScoped<IEventHandler<SaltChanged>, SaltEventHandler>();
            services.AddOpenApiDocument(settings => SwaggerController.SetOpenApiDocumentGeneratorSettings(settings, configuration));
            services.AddSwaggerDocument(settings => SwaggerController.SetSwaggerDocumentGeneratorSettings(settings, configuration));
            services.AddAuthentication(options => { }).AddHttpAuthentication("HttpAuthentication", o => { });
            services.AddOcelot()
                    .AddSingletonDefinedAggregator<SwaggerFilesAggregator>()
                    .AddSingletonDefinedAggregator<OpenApiFilesAggregator>()
                    .AddSingletonDefinedAggregator<AuthorizationsAggregator>()
                    .AddSingletonDefinedAggregator<RolesAggregator>()
                    .AddSingletonDefinedAggregator<GroupsAggregator>()
                    .AddPolly();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IConfiguration configuration)
        {
            app.UseOpenApi();
            app.UseSwaggerUi3(settings => SwaggerController.SetSettings(settings, configuration));
            app.UseReDoc(options =>
            {
                options.Path = "/redoc";
                options.DocumentPath = "/swaggerfiles.json";
            });

            var rewriteOptions = new RewriteOptions();
            rewriteOptions.AddRedirect("^$", "swagger");
            app.UseRewriter(rewriteOptions);
            app.UseAuthentication();
            app.UseCors("AllowAll");
            app.UseWebSockets();
            
            app.UseOcelot().Wait();
        }
    }
}

这是我的

AddHttpAuthentication
扩展方法:

public static AuthenticationBuilder AddHttpAuthentication(this AuthenticationBuilder builder, string authenticationScheme, Action<HttpAuthenticationOptions> configureOptions)
{
    return builder.AddScheme<HttpAuthenticationOptions, HttpAuthenticationHandler>(authenticationScheme, configureOptions);
}

HttpAuthentionOptions

public class HttpAuthenticationOptions : AuthenticationSchemeOptions
{
    public const string HandlerKey = "HttpAuthentication";
    public string Scheme => HandlerKey;
    public StringValues AuthKey { get; set; }
}

HttpAuthenticationHandler

public class HttpAuthenticationHandler : AuthenticationHandler<HttpAuthenticationOptions>
{
    private readonly IConfiguration configuration;

    public HttpAuthenticationHandler(IOptionsMonitor<HttpAuthenticationOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock, IConfiguration configuration)
        : base(options, logger, encoder, clock)
    {
        Console.WriteLine("Constructor -> HttpAuthenticationHandler");
        this.configuration = configuration;
    }

    /// <summary>
    /// Called when an Authentication by this authenticator is needed
    /// </summary>
    /// <returns>Task containing the result of the Authentication try</returns>
    protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
    {
        Console.WriteLine("Inside Method -> HandleAuthenticateAsync");
        try
        {
            if (SaltContainer.Salt == null)
                SaltContainer.SetSaltFromAuthenticationService(configuration);
        }
        catch (Exception e)
        {
          await SetErrorResponse($"Retrieving salt from authentication service failed: {e.Message} at {e.StackTrace}",
                                 HttpStatusCode.InternalServerError);
                                 
          return AuthenticateResult.Fail(e);
        }

        if (!Request.Headers.ContainsKey("Authorization") && !Request.Query.ContainsKey("Authorization")) 
        {
            await SetUnauthorizedResponse("Authorization token was not set");
            return AuthenticateResult.Fail("Authorization token was not set");
        }

        var token = Request.Headers.ContainsKey("Authorization") ? Request.Headers["Authorization"] : Request.Query["Authorization"];
        var authenticationResult = AuthenticationService.IsClientAuthenticated(token);

        if (authenticationResult.Failure != null)
            await SetUnauthorizedResponse(authenticationResult.Failure.Message);

        return authenticationResult;
    }

    private async Task SetUnauthorizedResponse(string message)
    {
        await SetErrorResponse(message, HttpStatusCode.Unauthorized);
    }

    private async Task SetErrorResponse(string message, HttpStatusCode statusCode)
    {
        var error = new ErrorResponse(statusCode, message);
        var bytes = Encoding.ASCII.GetBytes(JsonConvert.SerializeObject(error, new JsonSerializerSettings { ContractResolver = new CamelCasePropertyNamesContractResolver() }));
        Context.Response.ContentType = "application/json";
        Context.Response.StatusCode = (int)statusCode;
        await Context.Response.Body.WriteAsync(bytes);
    }
}
authentication signalr asp.net-core-3.1 .net-8.0 ocelot
1个回答
0
投票

将微服务从

.NET Core 3.0
升级到
.NET 8.0
后,我们遇到了一个问题:
**Ocelot**
库默认对所有路由应用身份验证。为了解决这个问题,我在
custom authentication scheme
项目中实现了
NoHttpAuthentication
并引入了新的身份验证方案
APIGateway

包括关键步骤:

  1. 自定义身份验证方案:开发并集成自定义的身份验证方案 身份验证处理程序用于处理不需要 HTTP 的路由 身份验证。
  2. 配置更新:修改了 ocelot.global.json 文件和 Startup.cs 支持新的身份验证方案并确保 路由按预期运行,无需进行不必要的身份验证。

这种方法可以更好地控制路由级别的身份验证,确保仅对必要的路由进行身份验证,而其他路由可以根据需要绕过身份验证。

Ocelot.global.json

{
    "Aggregates":
    [
      {
            "ReRouteKeys": [
                ".....",
            ],
            "UpstreamPathTemplate": "/.....",
            "Aggregator": "FilesAggregator",
            "AuthenticationOptions": {
                "AuthenticationProviderKey": "NoHttpAuthentication"
            }
      }
    ]
}

Startup.cs

services.AddAuthentication(options => { })
        .AddScheme<AuthenticationSchemeOptions, NoHttpAuthenticationHandler>(
            "NoHttpAuthentication", 
            options => { }
        );

NoHttpAuthenticationHandler.cs

public class NoHttpAuthenticationHandler : AuthenticationHandler<AuthenticationSchemeOptions>
{
    public NoHttpAuthenticationHandler(IOptionsMonitor<AuthenticationSchemeOptions> options, 
        ILoggerFactory logger, 
        UrlEncoder encoder, 
        ISystemClock clock) 
        : base(options, logger, encoder, clock)
    {
    }

protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
    {
        // No authentication needed, return success
        var authenticationTicket = new AuthenticationTicket(
            new ClaimsPrincipal(), 
            new AuthenticationProperties(), 
            "NoHttpAuthentication");

        return AuthenticateResult.Success(authenticationTicket);
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.