在 ASP.Net Core Identity 中刷新用户 cookie 票证

Posted

技术标签:

【中文标题】在 ASP.Net Core Identity 中刷新用户 cookie 票证【英文标题】:Refresh user cookie ticket in ASP.Net Core Identity 【发布时间】:2017-06-11 17:43:04 【问题描述】:

在 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 获取用户并刷新它。可以抽象为动作过滤器或扩展方法,或两者​​兼而有之 【参考方案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):

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

【讨论】:

以上是关于在 ASP.Net Core Identity 中刷新用户 cookie 票证的主要内容,如果未能解决你的问题,请参考以下文章

ASP.NET CORE 5.0 Identity 显示当前登录用户

Asp.Net core MVC6如何在Identity 3中初始添加角色

如何按照存储库模式在 Asp.Net Core 5.0 项目上实现 .Net Core Identity?

在 ASP.Net Core Identity 中刷新用户 cookie 票证

ASP.NET Core Identity 系列之六

在 ASP.NET Core 中使用基于本地存储的 JWT-Token 更改用户密码(ASP.Identity)