【问题标题】:Refresh user cookie ticket in ASP.Net Core Identity在 ASP.Net Core Identity 中刷新用户 cookie 票证
【发布时间】:2023-03-28 18:37:01
【问题描述】:

在 ASP.NET Core Web 应用程序的控制器中,我想刷新用户并声明存储在客户端上的 cookie 票证。

客户端经过身份验证和授权,ASP.NET Core Identity 将此信息存储在 cookie 票证中 - 现在在某些控制器操作中,我想刷新 cookie 中的数据。

SignInManager具有刷新RefreshSignInAsync的功能,但不接受HttpContext.User作为参数。

[HttpPost("[action]")]
[Authorize]
public async Task<IActionResult> Validate()
{
  // todo: update the Client Cookie
  await _signInManager.RefreshSignInAsync(User); // wrong type
}

如何刷新 cookie?

【问题讨论】:

  • 好吧,HttpContext.User 包含一个主体(ClaimsPrincipal,带有身份和声明)。 SignInManager 采用TUser,这取决于您用于配置身份的用户类型。只需从令牌中获取用户 ID 获取用户并刷新它。可以抽象为动作过滤器或扩展方法,或两者​​兼而有之

标签: c# asp.net-core asp.net-identity


【解决方案1】:
public static class HttpContextExtensions
{
    public static async Task RefreshLoginAsync(this HttpContext context)
    {
        if (context.User == null)
            return;

        // The example uses base class, IdentityUser, yours may be called 
        // ApplicationUser if you have added any extra fields to the model
        var userManager = context.RequestServices
            .GetRequiredService<UserManager<IdentityUser>>();
        var signInManager = context.RequestServices
            .GetRequiredService<SignInManager<IdentityUser>>();

        IdentityUser user = await userManager.GetUserAsync(context.User);

        if(signInManager.IsSignedIn(context.User))
        {
            await signInManager.RefreshSignInAsync(user);
        }
    }
}

然后在你的控制器中使用它

[HttpPost("[action]")]
[Authorize]
public async Task<IActionResult> Validate()
{
    await HttpContext.RefreshLoginAsync();
}

或者在动作过滤器中抽象它

public class RefreshLoginAttribute : ActionFilterAttribute
{
    public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
    {
        await context.HttpContext.RefreshLoginAsync();

        await next();
    }
}

然后在你的控制器中像这样使用它

[HttpPost("[action]")]
[Authorize]
[RefreshLogin] // or simpler [Authorize, RefreshLogin]
public async Task<IActionResult> Validate()
{
    // your normal controller code
}

【讨论】:

  • 很好——一件事,userManager.GetUserAsync 不返回类型 IdentityUser 而是返回 IdentityRoleEntity,它不能与 RefreshSignInAsync 作为参数一起使用。
  • IdentityRoleEntity 来自哪里?它似乎不是身份的一部分。你的类是从Identity 派生的吗?我问是因为 UserManager 来自 Identity 并且它的通用参数是用户模型,即默认配置中的 UserManager&lt;IdentityUser&gt;UserManager&lt;ApplicationUser&gt; 或任何你命名的类,当你将自己的字段扩展到它时。
  • 啊,是的-我使用了您的示例代码var userManager = context.RequestServices .GetRequiredService&lt;UserManager&lt;IdentityUser&gt;&gt;();,而没有用我自己的实体类型替换IdentityUser。我需要改用var userManager = context.RequestServices .GetRequiredService&lt;UserManager&lt;MyIdentityUserType&gt;&gt;();
  • Bisschen mitdenken musst du auch schon ;)
  • 我只是在更改用户角色后使用了扩展方法方法。 cookie 正在正确更新,但视图本身包含错误信息并使用用户的旧角色。我怎样才能做到这一点?
【解决方案2】:

如果用户已经注销(他们的访问令牌已过期但他们的刷新令牌仍然有效),这也是可能的。

重要提示:仅当您有“您想记住用户的设备吗?”时,以下内容才有效。在 cognito 配置中设置为“否”。如果有人知道如何使用它,请告诉我。

我们使用以下流程(js 客户端应用程序连接到 .NET Core API):

  1. 用户使用用户名/密码 (CognitoSignInManager&lt;CognitoUser&gt;.PasswordSignInAsync) 登录
  2. 客户端收到tokenuserIDrefreshToken并将它们存储在localStorage中。
  3. 当原始令牌过期(1 小时)时,客户端会从 API 收到 401 错误。
  4. 客户端使用userIDrefreshToken 调用另一个API 端点,然后后者又在我们的用户服务上调用以下代码。
  5. 如果刷新结果成功,我们返回新的令牌(AuthenticationResult.IdToken)。
  6. 客户端使用新令牌重复最初在 401 中出错的调用。

这是我们添加到用户服务中的代码:

public async Task<UserLoginResult> SignInRefreshAsync(string uid, string refreshToken)
{
    try
    {
        var result = await _cognitoIdentityProvider.InitiateAuthAsync(
            new InitiateAuthRequest
            {
                AuthFlow = AuthFlowType.REFRESH_TOKEN_AUTH,
                ClientId = _pool.ClientID,
                AuthParameters = new Dictionary<string, string>
                {
                    { "REFRESH_TOKEN", refreshToken },
                    { "SECRET_HASH", HmacSHA256(uid + _pool.ClientID, _options.UserPoolClientSecret) }
                }
            });

        if (!result.HttpStatusCode.Successful() || string.IsNullOrEmpty(result.AuthenticationResult?.IdToken))
            return new UserLoginResult(UserLoginStatus.Failed);

        return new UserLoginResult(UserLoginStatus.Success, uid, null, null, result.AuthenticationResult.IdToken, null);
    }
    catch
    {
        return new UserLoginResult(UserLoginStatus.Failed);
    }
}

private static string HmacSHA256(string data, string key)
{
    using (var sha = new System.Security.Cryptography.HMACSHA256(System.Text.Encoding.UTF8.GetBytes(key)))
    {
        var result = sha.ComputeHash(System.Text.Encoding.UTF8.GetBytes(data));
        return Convert.ToBase64String(result);
    }
}

IAmazonCognitoIdentityProvider _cognitoIdentityProvider 由 DI 解析。

AWSCognitoClientOptions _options = configuration.GetAWSCognitoClientOptions();IConfiguration configuration 也是从 DI 解析的。

UserLoginResult 是我们持有令牌和刷新令牌的类。显然,相应地进行调整。

请注意,根据您的配置是 Cognito,可能不需要设置 SECRET_HASH

【讨论】:

    猜你喜欢
    • 2021-08-25
    • 2020-06-08
    • 1970-01-01
    • 2017-10-29
    • 2018-12-30
    • 1970-01-01
    • 2016-11-18
    • 2018-10-25
    • 1970-01-01
    相关资源
    最近更新 更多