【问题标题】:Angular client, WebAPI server - cookie issue when userManager.GetUserAsync() methodAngular 客户端、WebAPI 服务器 - userManager.GetUserAsync() 方法时的 cookie 问题
【发布时间】:2025-12-11 18:30:01
【问题描述】:

我正在准备 WebAPI,其中客户端(Angular)通过 HTTP 请求登录和当前用户。当我从 Swagger 发送 POST 和 GET 请求时它工作正常(在 https://localhost:44322 上工作/swagger/index.html)。我收到了所有必要的答案,但是当我尝试从 Angular 这样做时会发生有趣的事情(适用于 https://localhost:4200)。 CORS 源已打开,允许标头,允许任何方法,允许凭据... 我想我遇到了一个与 cookie 相关的问题,因为当我在同一个浏览器窗口中打开两张卡片(swagger 和 angula)时,我可以找到所有的东西,但是当我将它们分开时,swagger 可以工作,但是 Angular 停止查看来自服务器端的 cookie。

我想我什么都试过了。我尝试在 HTTP 请求中使用凭据参数,我尝试对 CORS 进行参数化以允许打开 AllowCredentials();方法。没有任何效果。

所以,Swagger 可以发送如下请求。

我还实现了来自 Angular 的 HTTP 请求。 在login.component.ts下面

import { HttpClient } from '@angular/common/http';
import { Message } from '@angular/compiler/src/i18n/i18n_ast';
import { Component, OnInit } from '@angular/core';
import { first } from 'rxjs';
import { UserService } from '../user.service';

@Component({
  selector: 'app-login',
  templateUrl: './login.component.html',
  styleUrls: ['./login.component.css']
})
export class LoginComponent implements OnInit {

  response: any;
  currentUser = {
    firstName: "",
    lastName: ""
  };
  user: any;
  userLogin = {
    email: "",
    password: ""
  }

  firstName: string = "";
  lastName: string = "";

  constructor(private http: HttpClient, private service: UserService) { }

  ngOnInit(): void {
    this.getCurrentUser();
  }

  loginAction(): any {
    this.response = this.service.loginUser(this.userLogin);
    if(this.response){
      this.service.currentUser().subscribe((response: any) =>{
        this.currentUser.firstName = (response as any).firstName;
      });
    }
  }
  logoutAction():any{
    this.service.logoutUser();
  }

  getCurrentUser(){
    this.service.currentUser().subscribe((response: any) =>{
      this.currentUser.firstName = (response as any).firstName;
    });    
  }

}

还有 user.service.ts

export class UserService {

  readonly taskAPIUrl = "https://localhost:44322/api";
  
  constructor(private http: HttpClient) { }

  loginUser(userLogin :any) {
    return this.http.post("https://localhost:44322/api/UserLogin",userLogin).subscribe();
  }

  logoutUser(): any {
    return this.http.post<any>("https://localhost:44322/api/UserLogin/logout", {withCredentials: true}).subscribe();
  }

  currentUser(): any {
    return this.http.get<any>("https://localhost:44322/api/UserLogin/getCurrentUser", {withCredentials: true});
  }

这里是 Startup.cs

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.OpenApi.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using ToDoListAPI.Data;
using ToDoListAPI.Models;

namespace ToDoListAPI
{
    public class Startup
    {
        private string myAllowSpecificOrigins = "_myAllowSpecificOrigins";
        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)
        {

            services.AddControllers();
            services.AddSwaggerGen(c =>
            {
                c.SwaggerDoc("v1", new OpenApiInfo { Title = "ToDoListAPI", Version = "v1" });
            });

            services.AddDbContext<DataContext>(options =>
            {
                options.UseSqlServer(Configuration.GetConnectionString("ConnectionString"));
            });

            

            //Enable CORS
            services.AddCors(options =>
            {
                options.AddPolicy(name: myAllowSpecificOrigins,
                    builder =>
                    {
                        builder.WithOrigins("https://localhost:4200").
                        AllowAnyMethod().
                        AllowAnyHeader().
                        AllowCredentials();
                    });
            });

            services.AddIdentity<ApplicationUser, IdentityRole>(options =>
            {
                options.ClaimsIdentity.UserNameClaimType = "UserID";
            }).
                     AddEntityFrameworkStores<DataContext>().
                     AddDefaultTokenProviders();

            services.ConfigureApplicationCookie(options =>
            {
                options.Cookie.HttpOnly = false;
            });


        }

        // 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", "ToDoListAPI v1"));
            }

            app.UseHttpsRedirection();

            app.UseRouting();

            app.UseCors(myAllowSpecificOrigins);

            app.UseAuthentication();

            app.UseAuthorization();            

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

我发送 HTTP 请求的 UserLoginController.cs

using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
using System.Threading.Tasks;
using ToDoListAPI.Models;

// For more information on enabling Web API for empty projects, visit https://go.microsoft.com/fwlink/?LinkID=397860

namespace ToDoListAPI.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class UserLoginController : ControllerBase
    {
        private readonly UserManager<ApplicationUser> _userManager;
        private readonly SignInManager<ApplicationUser> _signInManager;

        public UserLoginController(UserManager<ApplicationUser> userManager,
                                    SignInManager<ApplicationUser> signInManager)
        {
            _userManager = userManager;
            _signInManager = signInManager;
        }
        // GET: api/<UserLoginController>
        [HttpGet]
        public IEnumerable<string> Get()
        {
            return new string[] { "value1", "value2" };
        }

        // GET api/<UserLoginController>/5
        [HttpGet("{id}")]
        public string Get(int id)
        {
            return "value";
        }

        [HttpGet]
        [Route("getCurrentUser")]
        public async Task<IActionResult> GetCurrentUser()
        {
            var user = await _userManager.GetUserAsync(User);

            if (user == null)
            {
                return Unauthorized();
            }
            return Ok(user);
        }

        // POST api/<UserLoginController>
        [HttpPost]
        public async Task<IActionResult> Login([FromBody] UserLogin userLoginDto)
        {
            var foundUser = await _userManager.FindByEmailAsync(userLoginDto.Email);
            if (foundUser == null)
            {
                return NotFound();
            }
            var result = await _signInManager.PasswordSignInAsync(
                foundUser, userLoginDto.Password, true, false);
            if (result.Succeeded)
            {
                return Ok();
            }
            return NotFound();
        }


        // POST api/<UserLoginController>
        // in progress
        [HttpPost]
        [Route("logout")]
        public async void Logout()
        {
            await _signInManager.SignOutAsync();
        }


        // DELETE api/<UserLoginController>/5
        [HttpDelete("{id}")]
        public void Delete(int id)
        {
        }
    }
}

请帮忙,我想我卡在某个地方了......

这是来自 Swagger 的 UserLogin 请求示例

这里来自 Angular 客户端

如您所见,Swagger 的请求和响应保持不变。最大的问题是当我发送 getCurrentUser() 请求时。

招摇:

和角度

【问题讨论】:

    标签: c# angular asp.net-core cookies swagger


    【解决方案1】:

    好的。对于角度来说,它应该看起来像这样。 在 user.service.ts 方法应该返回 Observalbe。

    举个例子:

    loginUser(userLogin : "here should be model class): Observable<Any> {
        return this.http.post("https://localhost:44322/api/UserLogin",userLogin).subscribe(repond => {return respond});
    
           return this.httpClient
          .post("https://localhost:44322/api/UserLogin",userLogin)
          .pipe(map(resposne =>{
              return resposne;
          }),
          catchError(error => {
            console.log(error);
          }));
      }
    

    在 login.component.ts 中的登录应该如下所示:

    loginAction() {
        this.service.loginUser(this.userLogin)
        .pipe(first())
        .subscribe( response =>{
            this.currentUser.firstName = response.firstName;
         }, error => {
            console.log(error); 
        });
      }
    

    对于控制器文件中的 GetCurrentUser 尝试解析你的 id 类型而不是 User 这个 User.Identity.Name 或 User.Identity.Id

    【讨论】: