【问题标题】:How to solve this circular dependency problem?如何解决这个循环依赖问题?
【发布时间】:2019-02-23 02:13:42
【问题描述】:

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

问题是 Token Provider 需要获取一个秘密值(密码)才能进行 HTTP 调用,而 Secret Provider 类需要获取一个 Token 才能调用 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;
    }
}

【问题讨论】:

  • 请贴出演示问题的代码。一个代码示例说了一千多个单词:)
  • 嗨@Steven,当然,我现在就发布。

标签: oop dependency-injection architecture


【解决方案1】:

让一个类实现两个接口。这两个职责是相互依赖的,因此将它们放在一个类中。这没有什么问题。

【讨论】:

    猜你喜欢
    • 2017-06-24
    • 1970-01-01
    • 2021-09-17
    • 2011-09-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-03-15
    相关资源
    最近更新 更多