如何解决这种循环依赖问题?

时间:2018-09-19 01:37:29

标签: oop dependency-injection architecture

我有两个组件接口,每个组件都需要另一个接口的功能。一个生成Oauth令牌,另一个从秘密提供者(Azure Key Vault)获取秘密。

问题是令牌提供者需要获取一个秘密值(密码)才能进行HTTP调用,而秘密提供者类需要获取一个令牌才能调用Azure。鸡肉和鸡蛋问题。

从我读过的其他问题来看,一个建议是创建原始2所依赖的第三类/接口,但是我不确定在这里如何工作。

任何帮助和建议将不胜感激。所有相关类/接口的代码如下所示。

public interface ISecretProvider
{
    string GetSecret(string secretName);
}

public interface ITokenProvider
{
    string GetKeyVaultToken();
}

public class OktaTokenProvider : ITokenProvider
{
    ISecretProvider _secretProvider;

    public string GetKeyVaultToken()
    {
        var tokenUrl = ConfigurationManager.AppSettings["KeyVault.Token.Url"];
        var clientId = ConfigurationManager.AppSettings["KeyVault.Token.ClientId"];
        var clientSecret = _secretProvider.GetSecret("ClientSecret");
        var scope = ConfigurationManager.AppSettings["KeyVault.Scope"];

        var token = GetToken(tokenUrl, clientId, clientSecret, scope);

        return token;
    }

    private string GetToken(string tokenUrl, string clientId, string clientSecret, string scope)
    {
        var clientCredentials = $"Basic {Convert.ToBase64String(Encoding.UTF8.GetBytes($"{clientId}:{clientSecret}"))}";

        string responseFromServer = string.Empty;
        bool success = false;
        int retryCount = 0;

        while (!success)
        {
            try
            {
                var tokenWebRequest = (HttpWebRequest)WebRequest.Create(tokenUrl);

                tokenWebRequest.Method = "POST";
                tokenWebRequest.Headers.Add($"Authorization:{clientCredentials}");
                tokenWebRequest.Headers.Add("Cache-control:no-cache");
                tokenWebRequest.ContentType = "application/x-www-form-urlencoded";

                using (var streamWriter = new StreamWriter(tokenWebRequest.GetRequestStream()))
                {
                    streamWriter.Write($"grant_type=client_credentials&scope={scope}");
                    streamWriter.Flush();
                    streamWriter.Close();
                }

                using (WebResponse response = tokenWebRequest.GetResponse())
                {
                    using (var dataStream = response.GetResponseStream())
                    {
                        using (StreamReader reader = new StreamReader(response.GetResponseStream()))
                        {
                            responseFromServer = reader.ReadToEnd();
                            reader.Close();
                        }
                        dataStream.Close();
                    }

                    response.Close();
                    response.Dispose();
                }

                success = true;
            }
            catch (Exception)
            {
                if (retryCount > 3)
                {
                    throw;
                }
                else
                {
                    retryCount++;
                }
            }
        }

        JToken token = JObject.Parse(responseFromServer);

        var accessToken = $"Bearer {token.SelectToken("access_token").ToString()}";

        return accessToken;
    }

}


public class KeyVaultSecretProvider : ISecretProvider
{
    ITokenProvider _tokenProvider;

    public KeyVaultSecretProvider(ITokenProvider tokenProvider)
    {
        _tokenProvider = tokenProvider;
    }

    public string GetSecret(string secretName)
    {
        var KeyVaultUrl = ConfigurationManager.AppSettings[Constants.KEYVAULT_ENDPOINT];
        var subscriptionKey = ConfigurationManager.AppSettings[Constants.KEYVAULT_SUBSCRIPTION_KEY];

        string responseFromServer = "";

        var requestedSecretUrl = $"{KeyVaultUrl}{secretName}";

        var secretWebRequest = (HttpWebRequest)WebRequest.Create(requestedSecretUrl);

        var accessToken = _tokenProvider.GetKeyVaultToken();

        secretWebRequest.Method = "GET";

        secretWebRequest.Headers.Add("authorization:" + accessToken);
        secretWebRequest.Headers.Add("cache-control:no-cache");
        secretWebRequest.Headers.Add("Ocp-Apim-Subscription-Key:" + subscriptionKey);

        using (WebResponse response = secretWebRequest.GetResponse())
        {
            using (var dataStream = response.GetResponseStream())
            {
                using (StreamReader reader = new StreamReader(response.GetResponseStream()))
                {
                    responseFromServer = reader.ReadToEnd();
                    reader.Close();
                }

                dataStream.Close();
            }

            response.Close();
            response.Dispose();
        }


        JToken secret = JObject.Parse(responseFromServer);

        var secretValue = secret.SelectToken("Secret").ToString();

        return secretValue;
    }
}

1 个答案:

答案 0 :(得分:0)

只有一个类同时实现两个接口。这两个职责是相互依赖的,因此将它们归为一类。这没什么问题。