You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何借助Identity与RabbitMQ实现微服务间用户认证?

核心问题解答

1. 登录后是否需要发队列消息?

只有当你需要在登录成功后触发跨服务的异步操作时才需要,比如:

  • 记录登录日志到专门的日志服务
  • 同步用户基础信息到Products服务的本地缓存(避免每次调用都查Auth服务)
    如果只是完成登录后调用Products服务的接口,完全不需要通过MQ传递信息。

2. 是否需要把JWT发队列给Products服务验证?

不需要。JWT的核心就是自包含、可独立验证:

  • Products服务只需要拿到Auth服务的公钥(或对称密钥),就能直接验证JWT的签名有效性、过期时间等,不需要依赖Auth服务或MQ传递验证请求。

分步实现逻辑

一、常规登录+Products服务调用流程(无MQ需求)

这是最常用的场景,不需要用到RabbitMQ:

  • 步骤1:完善Auth服务的JWT生成逻辑
    在Auth服务的登录接口中,用户验证通过后,生成包含用户ID、角色、权限等信息的JWT令牌,返回给前端。
    示例代码:

    [HttpPost("login")]
    public async Task<IActionResult> Login(LoginDto dto)
    {
        var user = await _userManager.FindByEmailAsync(dto.Email);
        if (user == null || !await _userManager.CheckPasswordAsync(user, dto.Password))
            return Unauthorized();
    
        // 生成JWT
        var claims = new List<Claim>
        {
            new Claim(ClaimTypes.NameIdentifier, user.Id),
            new Claim(ClaimTypes.Role, string.Join(",", await _userManager.GetRolesAsync(user)))
        };
        var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["Jwt:Key"]));
        var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
        var token = new JwtSecurityToken(
            issuer: _configuration["Jwt:Issuer"],
            audience: _configuration["Jwt:Audience"],
            claims: claims,
            expires: DateTime.Now.AddHours(1),
            signingCredentials: creds);
    
        return Ok(new { Token = new JwtSecurityTokenHandler().WriteToken(token) });
    }
    
  • 步骤2:Products服务配置JWT验证
    在Products服务的Program.cs中配置JWT验证,使用和Auth服务一致的密钥、签发者、受众:

    builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
        .AddJwtBearer(options =>
        {
            options.TokenValidationParameters = new TokenValidationParameters
            {
                ValidateIssuer = true,
                ValidateAudience = true,
                ValidateLifetime = true,
                ValidateIssuerSigningKey = true,
                ValidIssuer = builder.Configuration["Jwt:Issuer"],
                ValidAudience = builder.Configuration["Jwt:Audience"],
                IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["Jwt:Key"]))
            };
        });
    
    // 启用认证中间件
    app.UseAuthentication();
    app.UseAuthorization();
    

    之后在需要验证权限的接口上添加[Authorize]特性即可。

  • 步骤3:前端调用流程
    前端登录拿到JWT后,每次调用Products服务接口时,将JWT放在请求头的Authorization: Bearer {token}中,Products服务会自动完成令牌有效性验证。

二、需要MQ的场景(比如用户登录后同步信息到Products服务)

如果需要在用户登录后异步同步用户信息到Products服务,可基于你已有的MQ发送函数实现:

  • 步骤1:定义用户信息传输模型
    创建一个共享DTO类(可放在类库中让两个服务引用):

    public class UserLoggedInEvent
    {
        public string UserId { get; set; }
        public string UserName { get; set; }
        public List<string> Roles { get; set; }
    }
    
  • 步骤2:Auth服务登录成功后发送MQ消息
    在登录接口中,验证通过生成JWT后,序列化UserLoggedInEvent为JSON,调用你的SendMessageAsync发送(建议把队列名改成user-logined-events,更贴合业务):

    [HttpPost("login")]
    public async Task<IActionResult> Login(LoginDto dto)
    {
        // 省略用户验证和JWT生成逻辑...
    
        // 发送用户登录事件到MQ
        var loginEvent = new UserLoggedInEvent
        {
            UserId = user.Id,
            UserName = user.UserName,
            Roles = await _userManager.GetRolesAsync(user)
        };
        var message = JsonSerializer.Serialize(loginEvent);
        await SendMessageAsync(message);
    
        return Ok(new { Token = tokenString });
    }
    
  • 步骤3:Products服务实现MQ消费者
    在Products服务中创建后台服务监听队列,处理用户登录事件:

    public class UserLoginedConsumer : BackgroundService
    {
        private readonly IConnectionFactory _connectionFactory;
    
        public UserLoginedConsumer(IConnectionFactory connectionFactory)
        {
            _connectionFactory = connectionFactory;
        }
    
        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            using var connection = await _connectionFactory.CreateConnectionAsync(stoppingToken);
            using var channel = await connection.CreateChannelAsync(stoppingToken);
    
            await channel.QueueDeclareAsync(queue: "user-logined-events", durable: false, exclusive: false, autoDelete: false, arguments: null);
    
            var consumer = new AsyncEventingBasicConsumer(channel);
            consumer.Received += async (model, ea) =>
            {
                var body = ea.Body.ToArray();
                var message = Encoding.UTF8.GetString(body);
                var loginEvent = JsonSerializer.Deserialize<UserLoggedInEvent>(message);
    
                // 处理逻辑:比如将用户信息存入本地缓存(Redis/内存缓存)
                // _cache.Set($"user:{loginEvent.UserId}", loginEvent, TimeSpan.FromHours(1));
    
                await channel.BasicAckAsync(ea.DeliveryTag, multiple: false);
            };
    
            await channel.BasicConsumeAsync(queue: "user-logined-events", autoAck: false, consumer: consumer);
    
            await Task.Delay(Timeout.Infinite, stoppingToken);
        }
    }
    

    然后在Program.cs中注册消费者和连接工厂:

    builder.Services.AddSingleton<IConnectionFactory>(sp =>
        new ConnectionFactory { HostName = "localhost" });
    builder.Services.AddHostedService<UserLoginedConsumer>();
    

内容的提问来源于stack exchange,提问作者user28273827332

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 16:53:13