【无标题】JAVA 从 Vault 中读取数据

VaultReader类实现了从HashiCorpVault安全地读取数据的功能,利用SpringCache进行令牌管理。当令牌过期或无效时,会自动刷新。该类处理HTTP错误状态码,使用JSONPath提取数据,并提供了在SpringBoot应用中的使用示例。
摘要由CSDN通过智能技术生成

VaultReader Class

import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.Caching;
import org.springframework.http.*;
import org.springframework.web.client.HttpServerErrorException;
import org.springframework.web.client.HttpStatusCodeException;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;

import java.time.Duration;
import java.util.Base64;
import java.util.UUID;

import com.jayway.jsonpath.DocumentContext;
import com.jayway.jsonpath.JsonPath;
import com.jayway.jsonpath.PathNotFoundException;

public class VaultReader {

    private final Logger logger = LoggerFactory.getLogger(VaultReader.class);

    private final RestTemplate restTemplate;
    private final String vaultUrl;
    private final String username;
    private final String password;

    public VaultReader(String vaultUrl, String username, String password) {
        this.restTemplate = new RestTemplate();
        this.vaultUrl = vaultUrl;
        this.username = username;
        this.password = password;
    }

    @Cacheable(value = "vaultToken", key = "#username")
    public TokenWrapper getTokenWrapper(String username){
        return null;
    }

    // Remove the token from the cache when it's expired or revoked.
    @Caching(evict = {@CacheEvict(value="vaultToken", key="#username")})
    public void evictToken(String username){

    }

    public String readFromVault(String path, String jsonPath) throws HttpServerErrorException, PathNotFoundException, RestClientException {
        try {
            // Get an available token from cache or request a new one.
            TokenWrapper tokenWrapper= getTokenWrapper(username);

            if (isTokenExpired(tokenWrapper.expireTime)) {
                evictToken(username);
                tokenWrapper=getNewTokenWrapper(username,password);
            }

            // Set HTTP Authorization Header with the available token and execute requests.
            HttpHeaders headers = new HttpHeaders();
            headers.set("X-Vault-Token", tokenWrapper.token);
            HttpEntity<Object> entity = new HttpEntity<>(headers);

            String url = String.format("%s/v1/%s", vaultUrl, path);

            ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.GET, entity, String.class);
            if(response.getStatusCode() != HttpStatus.OK) {
                // Remove the token from cache when HTTP status code indicates an invalid or unauthorized request.
                evictToken(username);
                throw new HttpServerErrorException(response.getStatusCode(), "Vault request failed with status code " + response.getStatusCodeValue());
            }

            String responseBody = response.getBody();

            if (responseBody == null) {
                return null;
            }

            DocumentContext documentContext = JsonPath.parse(responseBody);
            Object result = documentContext.read(jsonPath);
            if(result == null){
                throw new PathNotFoundException(String.format("No result found for JSONPath %s", jsonPath));
            }

            return result.toString();

        } catch (HttpStatusCodeException e) {
            logger.error("HTTP request failed with status code {}: {}", e.getStatusCode(), e.getResponseBodyAsString());
            if (e.getStatusCode().is4xxClientError()) {
                evictToken(username);
            }
            throw e;
        } catch (RestClientException | IllegalStateException e) {
            logger.error("HTTP client exception: ", e);
            evictToken(username);
            throw e;
        }

    }

    private void storeTokenWrapper(String username, TokenWrapper tokenWrapper){
        // This method is intentionally left blank.
        // The Spring Cache will take care of storing the tokenWrapper object
        // into the cache with expiration settings.
    }

    private TokenWrapper getNewTokenWrapper(String username, String password) {
        HttpHeaders headers = new HttpHeaders();
        byte[] authBytes = Base64.getEncoder().encode(String.format("%s:%s", username, password).getBytes());
        String base64Auth = new String(authBytes);
        headers.set("Authorization", "Basic "+base64Auth);

        HttpEntity<String> requestEntity = new HttpEntity<>("", headers);

        String url = String.format("%s/v1/auth/userpass/login/%s", vaultUrl, username);
        ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.POST, requestEntity, String.class);
        if(response.getStatusCode() != HttpStatus.OK) {
            evictToken(username);
            throw new HttpServerErrorException(response.getStatusCode(), "Vault login request failed with status code " + response.getStatusCodeValue());
        }

        String token = response.getHeaders().getFirst("X-Vault-Token");
        String expireTimeString=response.getBody();

        JsonObject jsonObject = JsonParser.parseString(expireTimeString).getAsJsonObject();
        long ttl=jsonObject.get("auth").getAsJsonObject().get("lease_duration").getAsLong();

        TokenWrapper tokenWrapper=new TokenWrapper();
        tokenWrapper.token=token;
        // Calculate the expiration time based on the Time-To-Live (TTL) returned by Vault API.
        tokenWrapper.expireTime=System.currentTimeMillis()+ Duration.ofSeconds(ttl).toMillis()/2;

        storeTokenWrapper(username,tokenWrapper);

        return tokenWrapper;
    }

    private boolean isTokenExpired(long expireTime) {
        long timeDiff = expireTime - System.currentTimeMillis();
        return timeDiff <= 0;
    }

    class TokenWrapper{
        String token;
        long expireTime;
        UUID uuid=UUID.randomUUID();
    }
}

以下是在 Spring Boot 中如何使用 VaultReader class 的一个例子:

  1. 添加Maven依赖

首先,在你的 pom.xml 文件中添加以下 Maven 依赖:

<dependency>

    <groupId>org.springframework.boot</groupId>

    <artifactId>spring-boot-starter-web</artifactId>

    <version>${spring-boot.version}</version>

</dependency>



<dependency>

    <groupId>com.jayway.jsonpath</groupId>

    <artifactId>json-path</artifactId>

    <version>2.6.0</version>

</dependency>

其中 ${spring-boot.version} 应该替换为你所使用的 Spring Boot 版本。

  1. 声明 VaultReader

在某个需要使用 Vault 的类中,声明一个 VaultReader 对象并初始化它:

import org.springframework.beans.factory.annotation.Value;

import org.springframework.stereotype.Component;



@Component

public class MyService {



    private final VaultReader vaultReader;



    public MyService(@Value("${vault.url}") String url,

                     @Value("${vault.username}") String username,

                     @Value("${vault.password}") String password) {

        this.vaultReader = new VaultReader(url, username, password);

    }



    // 接下来实现自己的业务逻辑 ...

}

在这里,我们使用构造函数注入了 Vault URL、用户名和密码,并创建了一个 VaultReader 对象。

请注意,为了使用 @Value 注解注入属性值,你需要在你的 Spring Boot 应用程序中定义这些属性。这可以通过 application.properties application.yml 文件完成。例如,以下是一个配置文件示例:

vault:

  url: http://localhost:8200

  username: myusername

  password: mysecretpassword

  1. 使用 VaultReader 读取数据

现在,我们已经完成了 VaultReader 的初始化。接下来,使用 readFromVault(String path, String jsonPath) 方法从 Vault 中读取数据:

public class MyService {

    // ...



    public String readMySecret() {

        // Read the secret named "myapp/config/mysecret".

        // We want to extract the "password" field from this secret.

        String value = vaultReader.readFromVault("myapp/config/mysecret", "$.data.password");

        return value;

    }

}

在上面的例子中,我们通过调用 readFromVault() 方法,从 Vault 中读取名为“myapp/config/mysecret”的密钥,并从其数据中提取密码字段。

注意:在运行代码之前,确保你已经开启了 Vault Server 并创建了适当的密钥以供测试使用

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值