.NET CORE 5、VUE 2x、Docker、JWT 总是返回 401

Posted

技术标签:

【中文标题】.NET CORE 5、VUE 2x、Docker、JWT 总是返回 401【英文标题】:.NET CORE 5, VUE 2x, Docker, JWT returns always 401 【发布时间】:2021-11-12 03:27:32 【问题描述】:

我需要这个社区的帮助来解决一个非常繁琐的问题。 我正在部署一个使用 .net core 5 web api 作为后端,vue2x 作为前端的应用程序。 后端连接到 mongodb,一切都由 docker-compose 编排。 我有 JWT 身份验证和 CORS。

问题是当我在本地机器(Windows)上运行 docker-compose up 时,一切正常。

当我在我的服务器 (ubuntu) 上运行 docker-compose up 时,我无法进行身份验证,并且总是得到 401(即使使用邮递员)。

是的,我读过这个 https://docs.microsoft.com/en-gb/aspnet/core/fundamentals/middleware/?view=aspnetcore-5.0#order 和这个 ASP.NET core JWT authentication always throwing 401 unauthorized 和这个 ASP.Net Core API always returns 401 unauthorized whenever I send a request with Bearer token included.

非常烦人的是,docker 应该让我们摆脱“它在我的机器上工作”之类的问题,所以我真的不知道该往哪里砸了。

这里有一些代码

启动

  public class Startup

    readonly string LocalHostpecificOrigins = "_localhostSpecificOrigin";
    public Startup(IConfiguration configuration)
    
        Configuration = configuration;
    

    public IConfiguration Configuration  get; 

    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    
        #region db-config
        // requires using Microsoft.Extensions.Options
        services.Configure<BookstoreDatabaseSettings>(
    Configuration.GetSection(nameof(BookstoreDatabaseSettings)));

        services.AddSingleton<IBookstoreDatabaseSettings>(sp =>
            sp.GetRequiredService<IOptions<BookstoreDatabaseSettings>>().Value);

        services.AddSingleton<BookService>();


        // requires using Microsoft.Extensions.Options
        services.Configure<MisteryBoxDataBaseSettings>(
    Configuration.GetSection(nameof(MisteryBoxDataBaseSettings)));

        services.AddSingleton<IMisteryBoxDataBaseSettings>(sp =>
            sp.GetRequiredService<IOptions<MisteryBoxDataBaseSettings>>().Value);

        services.AddSingleton<MisteryBoxService>();


        services.Configure<UserDatabaseSettings>(
           Configuration.GetSection(nameof(UserDatabaseSettings)));

        services.AddSingleton<IDatabaseSettings>(sp =>
            sp.GetRequiredService<IOptions<UserDatabaseSettings>>().Value);

        services.AddSingleton<UserService>();
        #endregion

        services.AddCors(options =>
                
                    options.AddPolicy(name: LocalHostpecificOrigins,
                                      builder =>
                                      
                                          builder.WithOrigins(
                                                "http://localhost:8081", "http://localhost:8080",
                                                "https://localhost:5001", "http://localhost:5000",
                                                "http://188.166.121.146:8081", "https://188.166.121.146:8081")
                                          .WithMethods("PUT", "DELETE", "GET", "POST")
                                          .AllowAnyHeader()
                                          .AllowAnyMethod()
                                          .AllowCredentials();
                                      );
                );
        services.AddControllers();


        services.AddAuthentication(
            x =>
            
                x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
            )
            .AddJwtBearer(token =>
            
                token.RequireHttpsMetadata = false;
                token.SaveToken = true;
                token.TokenValidationParameters = new TokenValidationParameters
                
                    ValidateIssuerSigningKey = true,
                    IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(Configuration.GetSection("JwtKey").ToString())),
                    ValidateIssuer = false,
                    ValidateAudience = false
                ;
            );
        services.AddSwaggerGen(c =>
        
            c.SwaggerDoc("v1", new OpenApiInfo  Title = "Kool_Backend", Version = "v1" );
        );

    
    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    
        if (env.IsDevelopment())
        
            app.UseDeveloperExceptionPage();
            app.UseSwagger();
            app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "Kool_Backend v1"));
        

        app.UseHttpsRedirection();

        app.UseRouting();

        app.UseCors(LocalHostpecificOrigins);
        app.UseAuthentication();
        app.UseAuthorization();

        app.UseEndpoints(endpoints =>
        
            endpoints.MapControllers();
        );
    

用户控制器.cs

 [EnableCors("_localhostSpecificOrigin")]
[Authorize]
[Route("api/[controller]")]
[ApiController]
public class UserController : ControllerBase

    private readonly UserService _userService;
    public UserController(UserService service)
    
        _userService = service;
    
    [HttpGet]
    public ActionResult<List<User>> Get() =>
       _userService.Get();

    [HttpGet("id:length(24)", Name = "GetUser")]
    public ActionResult<User> Get(string id)
    
        var user = _userService.Get(id);

        if (user == null)
        
            return NotFound();
        

        return user;
    

    [HttpPost]
    public ActionResult<User> Create(User user)
    
        _userService.Create(user);

        return CreatedAtRoute("GetUser", new  id = user.Id.ToString() , user);
    


    [HttpPut(Name = "UpdateUser")]
    public IActionResult Update([FromBody] User userIn)
    
        var user = _userService.Get(userIn.Id);

        if (user == null)
        
            return NotFound();
        

        _userService.Update(user.Id, userIn);

        return NoContent();
    
    [AllowAnonymous]
    [Route("authenticate")]
    [HttpPost]
    public ActionResult Login([FromBody] User user)
    
        var token = _userService.Auhtenticate(user.Email, user.Password);

        if (token == null)
        
            return Unauthorized();
        
        return Ok(new  token, user );
    

Dockerfile

FROM mcr.microsoft.com/dotnet/aspnet:5.0 AS base
WORKDIR /app

FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build
WORKDIR /src
COPY ["./Kool-Backend/Kool-Backend.csproj", "."]
RUN dotnet restore "Kool-Backend.csproj"
COPY ./Kool-Backend .
WORKDIR "/src/"
RUN dotnet build "Kool-Backend.csproj" -c Release -o /app/build

FROM build AS publish
RUN dotnet publish "Kool-Backend.csproj" -c Release -o /app/publish

FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "Kool-Backend.dll"]

Docker 编写

      version: "3.4"

  services:
    mongodb:
      container_name: mongodb
      image: mongo
      volumes:
        - ~/mongo/data:/data/db
      ports:
        - "27017:27017"
    koolbackend:
      image: moscaen/koolbackend
      restart: unless-stopped
      build:
        context: .
        dockerfile: ./Kool-Backend/Dockerfile
      ports:
        - "5001:443"
        - "5000:80"
      environment:
        - ASPNETCORE_ENVIRONMENT=Development
        - ASPNETCORE_URLS=https://+:443;http://+:80
        - ASPNETCORE_HTTPS_PORT=5001
        - ASPNETCORE_Kestrel__Certificates__Default__Password=verysecretpassword
        - ASPNETCORE_Kestrel__Certificates__Default__Path=/https/Kool-Backend.pfx
      volumes:
        - ~/.aspnet/https:/https:ro
      depends_on:
        - mongodb

我省略了前端部分,因为我在与邮递员联系后端 api 时遇到问题。 当 docker-compose up 从我的本地机器(即 https://localhost:5001)运行但不在服务器上(即https://1xx.1xx.1xx.1xx:5001)运行时,它再次起作用

如果有人有什么想法,请告诉我。

谢谢,祝你有美好的一天!

PS:这里有一些截图

errors screenshot

【问题讨论】:

i.stack.imgur.com/xvPzd.png 显示 ERR_CERT_COMMON_NAME_INVALID。所以根本问题是 SSL 故障——一个 SSL 证书问题。 【参考方案1】:

最后我尝试了一个更宽松的 cors 政策,它看起来很有效。

我换了

  services.AddCors(options =>
            
                options.AddPolicy(name: LocalHostpecificOrigins,
                                  builder =>
                                  
                                      builder.WithOrigins(
                                            "http://localhost:8081", "http://localhost:8080",
                                            "https://localhost:5001", "http://localhost:5000",
                                            "http://188.166.121.146:8081", "https://188.166.121.146:8081")
                                      .WithMethods("PUT", "DELETE", "GET", "POST")
                                      .AllowAnyHeader()
                                      .AllowAnyMethod()
                                      .AllowCredentials();
                                  );
            );

 services.AddCors(options =>
        
            options.AddPolicy("AllowAll",
                builder =>  builder.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader(); );
        );

我已经把它移到下面了

services.AddSwaggerGen() ...

最后我在 Configure 方法中设置了 CORS

...
app.UseCors("AllowAll");
...

这允许我从控制器中删除属性。

...
[EnableCors("_localhostSpecificOrigin")]
...

简而言之,我仍然不知道是什么解决了这个问题,但这个设置现在可以工作了。

感谢大家的关注,祝您有美好的一天!

【讨论】:

以上是关于.NET CORE 5、VUE 2x、Docker、JWT 总是返回 401的主要内容,如果未能解决你的问题,请参考以下文章

ASP.NET Core 5 + SQL Server Docker 容器:无效的撰写项目

。NET Core 3.1 gRPC Docker:无法使原型路径相对

小5聊Vue与.net Core 如何接收List<T>泛型参数

带有 Vue JS 的 ASP.NET Core 5 从源访问 XMLHttpRequest 已被 CORS 策略阻止

Gitlab+Jenkins+Docker实现net core持续集成

docker启动.NET3.1与5.0的包