简体   繁体   English

如何使用 SignInManager 为 Blazor WebAssembly 实现模拟?

[英]How to implement impersonation using SignInManager for Blazor WebAssembly?

I am trying to implement impersonation for a Blazor WebAssembly Solution.我正在尝试为 Blazor WebAssembly 解决方案实施模拟。 I chose to use the SignInManager because it seemed like the best (and only?) solution for that.我选择使用 SignInManager,因为它似乎是最好的(也是唯一的?)解决方案。 I found some documentation and applied their solution but the newly created cookie which should contains the info for the impersonated user still contains the info from the initial user even though it is a new cookie...我找到了一些文档并应用了他们的解决方案,但是应该包含模拟用户信息的新创建的 cookie 仍然包含来自初始用户的信息,即使它是一个新的 cookie ...

For further information, I use a customized Identity model (based on Microsoft guidelines) to match my database structure.有关更多信息,我使用自定义身份模型(基于 Microsoft 指南)来匹配我的数据库结构。 The cookies are used with a MemoryCacheTicketStore to store user info in memory and not in the cookie (too much data to send them with every requests). cookie 与 MemoryCacheTicketStore 一起使用,将用户信息存储在内存中而不是 cookie 中(太多数据无法随每个请求一起发送)。 The cookie only contains the user's email and an expiration date. cookie 仅包含用户的电子邮件和到期日期。

So I tried to implement the examples I found in a Controller:所以我尝试实现我在控制器中找到的示例:

[HttpPost]
        [Authorize(Roles = "Data_User_Substitute")]
        public async Task<IActionResult> Impersonate(string email)
        {
            var user = await _userManager.FindByEmailAsync(email);
            if (user == null)
            {
                return Problem();
            }
 
            await _signInManager.SignOutAsync();
 
            var userPrincipal = await _signInManager.CreateUserPrincipalAsync(user);
            await this.HttpContext.SignInAsync(IdentityConstants.ApplicationScheme, userPrincipal);
            
            return Ok();
        }

The code goes smoothly through it, in the HttpContext.Request.Cookies the old cookies is set to expire by _signInManager.SignOutAsync and a new one is created by this.HttpContext.SignInAsync.代码顺利通过它,在 HttpContext.Request.Cookies 中,旧 cookie 由 _signInManager.SignOutAsync 设置为过期,而新的 cookie 由 this.HttpContext.SignInAsync 创建。

But the next requests send by the application, when entering the RetrieveAsync Method from the TicketStore, use a key that still contains the info of the old user.但是应用程序发送的下一个请求,当从 TicketStore 进入 RetrieveAsync 方法时,使用的密钥仍然包含旧用户的信息。 And thus the old user is still authenticated.因此旧用户仍然通过身份验证。

public async Task<AuthenticationTicket?> RetrieveAsync(string key)

The thing is that in this Controller I also have an endpoint for Login only and one for Logout only et they work perfectly fine when called by the application.问题是,在这个控制器中,我还有一个仅用于登录的端点和一个仅用于注销的端点,当它们被应用程序调用时,它们工作得非常好。 I think there must be something that is kept in memory and that is written in the cookie instead of the new user.我认为必须有一些东西保留在内存中,并且写在 cookie 中而不是新用户中。 But I could not find where.但我找不到在哪里。

What I expect is to have a new cookie containing the information about the impersonated user.我希望有一个新的 cookie,其中包含有关模拟用户的信息。

I found a roundabout solution that consists in using several requests to make the impersonation happen:我找到了一个迂回的解决方案,包括使用多个请求来进行模拟:

    1. Request an impersonation Token请求模拟令牌
    1. Logout the current user注销当前用户
    1. Impersonate using the token使用令牌模拟
        class ImpersonationModel
        {
            public string Email { get; set; }
            public DateTimeOffset Timestamp { get; set; }
        }      
      
        [HttpGet]
        [Authorize(Roles = "Data_User_Substitute")]
        public string ImpersonationToken([FromQuery] string email, [FromServices] CustomerAreaServerSettings settings)
        {
            using (var encryptor = SymmetricEncryptor.FromString("key")
            {
                var model = new ImpersonationModel { Email = email, Timestamp = DateTimeOffset.UtcNow };
                string token = encryptor.Base64Encrypt(JsonSerializer.Serialize(model));
                return token;
            }
        }

        [HttpPost]
        [AllowAnonymous]
        public async Task<IActionResult> Impersonate([FromQuery] string token, [FromServices] CustomerAreaServerSettings settings)
        {
            if (string.IsNullOrWhiteSpace(token)) throw new ArgumentException($"'{nameof(token)}' cannot be null or whitespace.", nameof(token));
            
            string? email = null;
            using (var encryptor = SymmetricEncryptor.FromString("key")
            {
                string json = encryptor.Base64Decrypt(token);
                var model = JsonSerializer.Deserialize<ImpersonationModel>(json);
                if (model is null) throw new ArgumentException("Invalid token");
                if (model.Timestamp.AddMinutes(15) < DateTimeOffset.UtcNow) throw new Exception("Authentication token has expired");
                email = model.Email;
            }

            var user = await _userManager.FindByEmailAsync(email);
            if (user == null)
                return BadRequest("User does not exist");

            await _signInManager.SignInAsync(user, false);
            var newUser = this.HttpContext.User;

            return Ok();
        }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM