@Value 未注入 SpringBoot 测试上下文

时间:2021-04-04 18:32:22

标签: java azure spring-boot spring-mvc azure-keyvault

我正在使用 azure keyvault 来提取我的应用程序属性。我正在使用 spring @value 注释通过将占位符放置在 application.properties 文件中来设置密钥库中的属性值。在我的主应用程序上下文中,我能够提取属性并测试应用程序流程。在测试上下文中,它抛出了一些问题,说没有注入保险库属性。这是我的属性 bean 类的样子,以及问题的堆栈跟踪。我试图模拟 ControllerTest 类中的 KeyVaultProperties 仍然有同样的问题。

KeyVault.java

@Data
@Component
public class KeyVaultProperties {

    @Value("${by-pass-token}")
    private String token;

    @Value("${backend-clients}")
    private String clients;

}

ControllerTest.java

    @SpringBootTest
    @SpringBootConfiguration
    @AutoConfigureMockMvc
    public class ControllerTest {
         
    @Autowired
    Controller controller;

    @Autowired
    private MockMvc mockMvc;

    @Test
    public void contextLoads() throws Exception {
        assertThat(controller).isNotNull();
    }

    }

Controller.java

@RestController
@Slf4j
@RequestMapping("/api/test")
public class Controller {

   @GetMapping(value = "/hello")
   public String getString() {
     return "Hello";
    }
}

AuthConfiguration.java

@Slf4j
@Component
public class AuthConfiguration extends HandlerInterceptorAdapter {

    @Autowired
    private KeyVaultProperties keyVaultProperties;

        private static final String CORRELATION_ID_LOG_VAR_NAME = "correlationId";
    private static final String CORRELATION_ID_HEADER_NAME = "Correlation-Id";

  @PostConstruct
    public void setup() {
        System.out.println("-------@PostConstruct------setup----------------");
        sub = keyVaultProperties.getClients();
        ByPass = keyVaultProperties.getAuthByPassToken();
    }

    @Override
    public boolean preHandle(final HttpServletRequest request, final HttpServletResponse response, final Object handler)
            throws Exception {
        System.out.println("-------preHandle----------------------");
        final Boolean isValidToken;
        final String correlationId = getCorrelationIdFromHeader(request);
            log.info("correlationId:{}",correlationId);
            MDC.put(CORRELATION_ID_LOG_VAR_NAME, correlationId);

        return true;
    }
    @Override
    public void afterCompletion(final HttpServletRequest request, final HttpServletResponse response,
                                final Object handler, final Exception ex) {
        System.out.println("-------afterCompletion----------------------");
        MDC.remove(CORRELATION_ID_LOG_VAR_NAME);
    }
    private String getCorrelationIdFromHeader(final HttpServletRequest request) {
        String correlationId = request.getHeader(CORRELATION_ID_HEADER_NAME);
        if (correlationId == null) {
            correlationId = generateUniqueCorrelationId();
        }
        return correlationId;
    }



}

app/src/main/resources/application.properties

by-pass-token = ${BY-PASS-TOKEN}
backend-clients = ${CLIENTS}
azure.keyvault.enabled=true

堆栈跟踪:

2021-04-04 13:28:03.640 [main]  ERROR org.springframework.boot.SpringApplication - Application run failed
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'AuthConfiguration': Unsatisfied dependency expressed through field 'KeyVaultProperties'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'KeyVaultProperties': Injection of autowired dependencies failed; nested exception is java.lang.IllegalArgumentException: Could not resolve placeholder 'by-pass-token' in value "${by-pass-token}"
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject

1 个答案:

答案 0 :(得分:1)

您可以通过 Azure AD 进行身份验证,将属性值设置为 Azure Key Vault。

注意:为了让您的应用程序能够访问 Key Vault 内容,您必须在 Key Vault 中为您的应用程序设置适当的权限。导航到 Azure Key Vault > 访问策略 > 添加访问策略 > 在选择主体中选择您的应用程序。

依赖关系:

<dependency>
    <groupId>com.microsoft.azure</groupId>
    <artifactId>azure</artifactId>
    <version>1.3.0</version>
</dependency>
<dependency>
    <groupId>com.microsoft.azure</groupId>
    <artifactId>azure-keyvault</artifactId>
    <version>1.0.0</version>
</dependency>

通过基于 client credentials flow 的 AzureAD 连接到 Key Vault:

public class ClientSecretKeyVaultCredential extends KeyVaultCredentials
{
    private String clientId;
    private String clientKey;

    public ClientSecretKeyVaultCredential( String clientId, String clientKey ) {
        this.clientId = clientId;
        this.clientKey = clientKey;
    }

    @Override
    public String doAuthenticate(String authorization, String resource, String scope) {
        AuthenticationResult token = getAccessTokenFromClientCredentials(
                authorization, resource, clientId, clientKey);
        return token.getAccessToken();
    }

    private static AuthenticationResult getAccessTokenFromClientCredentials(
            String authorization, String resource, String clientId, String clientKey) {
        AuthenticationContext context = null;
        AuthenticationResult result = null;
        ExecutorService service = null;
        try {
            service = Executors.newFixedThreadPool(1);
            context = new AuthenticationContext(authorization, false, service);
            ClientCredential credentials = new ClientCredential(clientId, clientKey);
            Future<AuthenticationResult> future = context.acquireToken(
                    resource, credentials, null);
            result = future.get();
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            service.shutdown();
        }

        if (result == null) {
            throw new RuntimeException("authentication result was null");
        }
        return result;
    }
}

访问密钥保管库:

您可以使用 client.setSecret("Secret-Name", "value") 来设置您的属性。

// ClientSecretKeyVaultCredential is the implementation of KeyVaultCredentials
KeyVaultClient client = new KeyVaultClient(
        new ClientSecretKeyVaultCredential(clientId, clientKey));

// KEYVAULT_URL is the location of the keyvault to use: https://<yourkeyvault>.vault.azure.net
SecretBundle secret = client.getSecret( KEYVAULT_URL, "Secret-name" );
log( secret.value() );
相关问题