无法从托管服务的缓存中附加身份验证令牌

时间:2019-08-22 19:28:21

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

我是.net的新手,我真的很想了解应用程序的这种行为。我有一个名为GetOrg()的函数,该函数基本上会请求API端点并获取数据,以获取我需要在每个请求中传递Auth令牌的数据。要获取Auth令牌,我还有另一个函数调用GetAccessToken获取令牌并将其保存在缓存中。我创建了一个名为httpclient的令牌,该令牌将令牌附加到对NonProductionEnv客户端的每个请求中。

现在的问题是,当我在托管服务中设置GetOrg()时,如下所示,它没有附加令牌并请求没有auth令牌的API端点,但是如果我在控制器中设置了GetOrg(),它就可以正常工作。 / p>

托管服务:

public class TokenService : DelegatingHandler, IHostedService
{
    public IConfiguration Configuration { get; }
    protected IMemoryCache _cache;
    private Timer _timer;
    public IHttpClientFactory _clientFactory;
    protected HttpClient _client_NP;
    private readonly IServiceScopeFactory _scopeFactory;

    public TokenService(IConfiguration configuration, IMemoryCache memoryCache, IHttpClientFactory clientFactory, IServiceScopeFactory scopeFactory)
    {
        Configuration = configuration;
        _cache = memoryCache;
        _clientFactory = clientFactory;
        _scopeFactory = scopeFactory;

        // NamedClients foreach Env.
        _client_NP = _clientFactory.CreateClient("NonProductionEnv");
    }

    public Task StartAsync(CancellationToken cancellationToken)
    {
        _timer = new Timer(GetAccessToken, null, 0, 3300000);
        // Thread.Sleep(2000);
        _timer = new Timer(Heartbeat, null, 1000, 1000);
        return Task.CompletedTask;
    }

    public Task StopAsync(CancellationToken cancellationToken)
    {
        //Timer does not have a stop. 
        _timer?.Change(Timeout.Infinite, 0);
        return Task.CompletedTask;
    }

    public async Task<Token> GetToken(Uri authenticationUrl, Dictionary<string, string> authenticationCredentials)
    {
        HttpClient client = new HttpClient();
        FormUrlEncodedContent content = new FormUrlEncodedContent(authenticationCredentials);
        HttpResponseMessage response = await client.PostAsync(authenticationUrl, content);

        if (response.StatusCode != System.Net.HttpStatusCode.OK)
        {
            string message = String.Format("POST failed. Received HTTP {0}", response.StatusCode);
            throw new ApplicationException(message);
        }

        string responseString = await response.Content.ReadAsStringAsync();
        Token token = JsonConvert.DeserializeObject<Token>(responseString);

        return token;
    }

    private void GetAccessToken(object state)
    {
        Dictionary<string, string> authenticationCredentials_np = Configuration.GetSection("NonProductionEnvironment:Credentials").GetChildren().Select(x => new KeyValuePair<string, string>(x.Key, x.Value)).ToDictionary(x => x.Key, x => x.Value);
        Token token_np = GetToken(new Uri(Configuration["NonProductionEnvironment:URL"]), authenticationCredentials_np).Result;

        _cache.Set("np", token_np.AccessToken);
    }

    public void Heartbeat(object state)
    {
        // Discard the result
        _ = GetOrg();
    }

    public async Task GetOrg()
    {
        var request = new HttpRequestMessage(HttpMethod.Get, "organizations");
        var response = await _client_NP.SendAsync(request);
        var json = await response.Content.ReadAsStringAsync();
        OrganizationsClass.OrgsRootObject model = JsonConvert.DeserializeObject<OrganizationsClass.OrgsRootObject>(json);

        using (var scope = _scopeFactory.CreateScope())
        {
            var _DBcontext = scope.ServiceProvider.GetRequiredService<PCFStatusContext>();

            foreach (var item in model.resources)
            {
                var g = Guid.Parse(item.guid);
                var x = _DBcontext.Organizations.FirstOrDefault(o => o.OrgGuid == g);
                if (x == null)
                {
                    _DBcontext.Organizations.Add(new Organizations
                    {
                        OrgGuid = g,
                        Name = item.name,
                        CreatedAt = item.created_at,
                        UpdatedAt = item.updated_at,
                        Timestamp = DateTime.Now,
                        Foundation = 3
                    });
                }
                else if (x.UpdatedAt != item.updated_at)
                {
                    x.CreatedAt = item.created_at;
                    x.UpdatedAt = item.updated_at;
                    x.Timestamp = DateTime.Now;
                }
            }

            await GetSpace();
            await _DBcontext.SaveChangesAsync();
        }
    }

}

启动中的命名客户端:

    services.AddHttpClient("NonProductionEnv", client =>
{
    client.BaseAddress = new Uri(Configuration["NonProductionEnvironment:NP_API_URL"]);
    client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", (String)_cache.Get("np"));
});

下面的日志每秒生成一次,因为我需要每秒调用api端点。

  

现在收听:https://localhost:5001现在收听:   http://localhost:5000应用程序已启动。按Ctrl + C关闭。   信息:System.Net.Http.HttpClient.NonProductionEnv.LogicalHandler [100]         开始处理HTTP请求GET https://api.sample.io/v3/organizations信息:   System.Net.Http.HttpClient.NonProductionEnv.ClientHandler [100]         发送HTTP请求GET https://api.sample.io/v3/organizations信息:System.Net.Http.HttpClient.NonProductionEnv.ClientHandler [101]         53.3973毫秒后收到HTTP响应-未经授权

PS:我对.net及其工作原理并不了解。

1 个答案:

答案 0 :(得分:0)

您的HttpClient正在注册,并且在应用启动时从缓存中检索了静态值。此时,您的托管服务尚未运行,因此缓存中没有任何值。一旦缓存中终于有了一个值,标头就已经设置很久了,您再也不会重置它。

这里实际上完全不需要缓存。您也不需要在实际的客户注册中设置Authorization标头。相反,只需将您的GetAccessToken方法修改为:

private void GetAccessToken(object state)
{
    Dictionary<string, string> authenticationCredentials_np = Configuration.GetSection("NonProductionEnvironment:Credentials").GetChildren().Select(x => new KeyValuePair<string, string>(x.Key, x.Value)).ToDictionary(x => x.Key, x => x.Value);
    Token token_np = GetToken(new Uri(Configuration["NonProductionEnvironment:URL"]), authenticationCredentials_np).Result;

    _client_NP.DefaultRequestHeaders.Add("Authorization", $"Bearer {token_np.AccessToken}");
}