Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/asp.net-core/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/visual-studio-2010/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Asp.net 在OIDC中同时刷新访问令牌的多个请求_Asp.net_Asp.net Core_Identityserver4_Asp.net Core Identity_Openid Connect - Fatal编程技术网

Asp.net 在OIDC中同时刷新访问令牌的多个请求

Asp.net 在OIDC中同时刷新访问令牌的多个请求,asp.net,asp.net-core,identityserver4,asp.net-core-identity,openid-connect,Asp.net,Asp.net Core,Identityserver4,Asp.net Core Identity,Openid Connect,在一个页面应用程序同时进行多个api调用的特定情况下,我对更新刷新令牌有点头疼。我有一个水疗中心,它有一个由以下部分组成的堆栈 Html/JS SPA->MVC应用程序->WebAPI 我利用混合流,当用户登录页面时,我将id\u令牌访问令牌和刷新令牌存储在会话cookie中 我使用一个HttpClient,它有两个delegatinghandler来与web API对话。其中一个委托处理程序只是将访问令牌添加到授权头中。另一个在这之前运行,并检查访问令牌上剩余的生存期。如果访问令牌的剩余时间有

在一个页面应用程序同时进行多个api调用的特定情况下,我对更新刷新令牌有点头疼。我有一个水疗中心,它有一个由以下部分组成的堆栈

Html/JS SPA->MVC应用程序->WebAPI

我利用混合流,当用户登录页面时,我将
id\u令牌
访问令牌和
刷新令牌
存储在会话cookie中

我使用一个
HttpClient
,它有两个
delegatinghandler
来与web API对话。其中一个委托处理程序只是将访问令牌添加到授权头中。另一个在这之前运行,并检查访问令牌上剩余的生存期。如果访问令牌的剩余时间有限,则使用刷新令牌获取新凭据并将其保存回我的会话

以下是OidcTokenRefreshHandler的代码

public class OidcTokenRefreshHandler : DelegatingHandler
{
    private readonly IHttpContextAccessor _httpContextAccessor;
    private readonly OidcTokenRefreshHandlerParams _handlerParams;

    public OidcTokenRefreshHandler(IHttpContextAccessor httpContextAccessor, OidcTokenRefreshHandlerParams handlerParams)
    {
        _httpContextAccessor = httpContextAccessor;
        _handlerParams = handlerParams;
    }

    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request,
        CancellationToken cancellationToken)
    {
        var accessToken = await _httpContextAccessor.HttpContext.GetTokenAsync("access_token");
        var handler = new JwtSecurityTokenHandler();
        var accessTokenObj = handler.ReadJwtToken(accessToken);
        var expiry = accessTokenObj.ValidTo;

        if (expiry - TimeSpan.FromMinutes(_handlerParams.AccessTokenThresholdTimeInMinutes) < DateTime.UtcNow )
        {
            await RefreshTokenAsync(cancellationToken);
        }

        return await base.SendAsync(request, cancellationToken);
    }

    private async Task RefreshTokenAsync(CancellationToken cancellationToken)
    {
        var client = new HttpClient();

        var discoveryResponse = await client.GetDiscoveryDocumentAsync(_handlerParams.OidcAuthorityUrl, cancellationToken);
        if (discoveryResponse.IsError)
        {
            throw new Exception(discoveryResponse.Error);
        }

        var refreshToken = await _httpContextAccessor.HttpContext.GetTokenAsync(OpenIdConnectParameterNames.RefreshToken);
        var tokenResponse = await client.RequestRefreshTokenAsync(new RefreshTokenRequest
        {
            Address = discoveryResponse.TokenEndpoint,
            ClientId = _handlerParams.OidcClientId,
            ClientSecret = _handlerParams.OidcClientSecret,
            RefreshToken = refreshToken
        }, cancellationToken);
        if (tokenResponse.IsError)
        {
            throw new Exception(tokenResponse.Error);
        }

        var tokens = new List<AuthenticationToken>
        {
            new AuthenticationToken
            {
                Name = OpenIdConnectParameterNames.IdToken,
                Value = tokenResponse.IdentityToken
            },
            new AuthenticationToken
            {
                Name = OpenIdConnectParameterNames.AccessToken,
                Value = tokenResponse.AccessToken
            },
            new AuthenticationToken
            {
                Name = OpenIdConnectParameterNames.RefreshToken,
                Value = tokenResponse.RefreshToken
            }
        };

        // Sign in the user with a new refresh_token and new access_token.
        var info = await _httpContextAccessor.HttpContext.AuthenticateAsync("Cookies");
        info.Properties.StoreTokens(tokens);
        await _httpContextAccessor.HttpContext.SignInAsync("Cookies", info.Principal, info.Properties);
    }
}
公共类OidcTokenRefreshHandler:DelegatingHandler
{
专用只读IHttpContextAccessor\u httpContextAccessor;
私有只读OidcTokenRefreshHandlerParams\u handlerParams;
公共OidcTokenRefreshHandler(IHttpContextAccessor httpContextAccessor,OidcTokenRefreshHandlerParams handlerParams)
{
_httpContextAccessor=httpContextAccessor;
_handlerParams=handlerParams;
}
受保护的覆盖异步任务SendAsync(HttpRequestMessage请求,
取消令牌(取消令牌)
{
var accessToken=await_httpContextAccessor.HttpContext.GetTokenAsync(“访问令牌”);
var handler=新的JwtSecurityTokenHandler();
var accessTokenObj=handler.ReadJwtToken(accessToken);
var到期=accessTokenObj.ValidTo;
if(expire-TimeSpan.frommins(_handlerParams.AccessTokenThresholdTimeInMinutes)
问题是,许多电话几乎同时打到了这一点。所有这些调用都将同时命中刷新端点。它们都将检索新的有效访问令牌,应用程序将继续工作。但是,如果同时发生3个请求,将创建三个新的刷新令牌,其中只有一个有效。由于应用程序的异步性质,我不能保证存储在会话中的刷新令牌实际上是最新的刷新令牌。下次需要刷新时,刷新令牌可能无效(通常是无效的)

到目前为止,我对可能的解决方案的想法

  • 在使用互斥锁或类似工具检查访问令牌时锁定。然而,当不同的用户在不同的会话中使用它时(据我所知),这可能会造成阻塞。如果我的MVC应用程序跨越多个实例,它也不起作用

  • 更改以使刷新令牌在使用后保持有效。因此,三者中的哪一个被使用并不重要

以上哪一个更好,或者有人有真正聪明的选择吗


非常感谢

当您的所有请求都来自同一个SPA时,最好是在浏览器中同步它们,并解决服务器端的问题。每次客户端代码需要令牌时,都返回一个承诺。对所有请求使用相同的promise实例,因此它们都通过对服务器的唯一请求得到解决

不幸的是,如果您通过本地API代理所有请求,并且从不将承载者传递给SPA,我的想法就行不通了


但是,如果你让你的刷新令牌绝对安全(永远不要把它发送到前端),我看不出任何问题,使其可重用。在这种情况下,您可以打开滑动选项以执行较少的续费请求。

非常感谢您的想法,但是,正如您自己所说,这将需要向SPA提供访问令牌。这不是出于安全原因,我使用的是混合流。当您处于如此高的安全级别时,