Spring Vault 客户端 - 无法连接到本地开发 Vault 服务器
Spring Vault client - not able to connect to local dev Vault server
我在本地安装了 Vault。根据这个官方教程 https://learn.hashicorp.com/vault/
,我能够启动本地开发服务器和 write/read 进入 Vault kv 的一些秘密
然后我想创建一些非常基本的 Java/Spring 引导演示客户端,它将连接到我的本地 Vault 开发服务器以 [=63=] 秘密。我阅读了 Baeldung 教程以获取灵感 https://www.baeldung.com/spring-vault。
这是我的金库-config.properties:
vault.uri=http://127.0.0.1:8200
vault.token=s.EXg6MQwUuB63Z7Xra4zybOut(服务器最近一次启动后生成的token)
然后服务 class:
@Service
public class CredentialsService {
@Autowired
private VaultTemplate vaultTemplate;
public void secureCredentials(Credentials credentials) throws URISyntaxException {
vaultTemplate.write("credentials/myapp", credentials);
}
public Credentials accessCredentials() throws URISyntaxException {
VaultResponseSupport<Credentials> response = vaultTemplate.read("credentials/myapp", Credentials.class);
return response.getData();
}
}
配置class:
@Configuration
public class VaultConfig extends AbstractVaultConfiguration {
@Override
public ClientAuthentication clientAuthentication() {
return new TokenAuthentication("s.EXg6MQwUuB63Z7Xra4zybOut");
}
@Override
public VaultEndpoint vaultEndpoint() {
return VaultEndpoint.create("host", 8200);
}
}
还有这个:
@Configuration
@PropertySource(value = { "vault-config.properties" })
@Import(value = EnvironmentVaultConfiguration.class)
public class VaultEnvironmentConfig {
}
一个域对象:
public class Credentials {
private String username;
private String password;
public Credentials() {
}
public Credentials(String username, String password) {
this.username = username;
this.password = password;
}
public String getUsername() {
return username;
}
public String getPassword() {
return password;
}
@Override
public String toString() {
return "Credential [username=" + username + ", password=" + password + "]";
}
}
最后是我的主要 Spring 启动 class:
@RestController
@ComponentScan
@SpringBootApplication
public class SpringVaultTutorial {
@Autowired
CredentialsService credentialsService;
@RequestMapping("/")
String home() throws URISyntaxException {
Credentials credentials = new Credentials("oliver","exxeta123");
credentialsService.secureCredentials(credentials);
return credentialsService.accessCredentials().getUsername().toString();
}
public static void main(String[] args) {
SpringApplication.run(SpringVaultTutorial.class, args);
}
}
Main class 应该写入 secret 然后立即读取它并打印用户名。但我收到此错误消息:
出现意外错误(类型=内部服务器错误,状态=500)。
I/O POST 请求“https://host:8200/v1/credentials/myapp”时出错:主机;嵌套异常是 java.net.UnknownHostException: host
有人知道哪里出了问题吗?
编辑:
根据 Arun 的建议,我遵循了本教程 https://drissamri.be/blog/java/enable-https-in-spring-boot/
我一直在尝试这两种方法。
1)修改application.properties:
server.port: 8443
server.ssl.key-store: keystore.p12
server.ssl.key-store-password: oliver
server.ssl.keyStoreType: PKCS12
server.ssl.keyAlias: tomcat
security.require-ssl=true
修改后,当我调用 https://localhost:8443 时,出现异常:
javax.net.ssl.SSLException: 无法识别的 SSL 消息,明文连接?
在 sun.security.ssl.InputRecord.handleUnknownRecord(InputRecord.java:710) ~[na:1.8.0_121]
在 sun.security.ssl.InputRecord.read(InputRecord.java:527) ~[na:1.8.0_121]
在 sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:973) ~[na:1.8.0_121]
在 sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1375) ~[na:1.8.0_121]
在 sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1403) ~[na:1.8.0_121]
在 sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1387) ~[na:1.8.0_121]
2) 基于教程的第二种方法是添加 ConnectorConfig class:
@Configuration
public class ConnectorConfig {
@Bean
public ServletWebServerFactory servletContainer() {
TomcatServletWebServerFactory tomcat =
new TomcatServletWebServerFactory() {
@Override
protected void postProcessContext(Context context) {
SecurityConstraint securityConstraint = new SecurityConstraint();
securityConstraint.setUserConstraint("CONFIDENTIAL");
SecurityCollection collection = new SecurityCollection();
collection.addPattern("/*");
securityConstraint.addCollection(collection);
context.addConstraint(securityConstraint);
}
};
tomcat.addAdditionalTomcatConnectors(redirectConnector());
return tomcat;
}
private Connector redirectConnector() {
Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol");
connector.setScheme("http");
connector.setPort(8090);
connector.setSecure(false);
connector.setRedirectPort(8443);
return connector;
}
}
但是在调用 localhost:8090 将我重定向到 https://localhost:8443 之后,我得到了同样的错误:javax.net.ssl.SSLException:无法识别的 SSL 消息,纯文本连接?
在 sun.security.ssl.InputRecord.handleUnknownRecord(InputRecord.java:710) ~
现在的问题是:我是否还必须在 Vault 端配置一些关于证书的东西?或者您认为 Java 客户端可能存在证书问题?但我认为如果存在 Java 证书问题,启动期间就会抛出异常。
UnknownHostException 是由于名称 'host' 没有可用的服务器。您可以在主机文件映射中添加一个条目到本地主机。或尝试在创建保管库时更改主机名
@Override
public VaultEndpoint vaultEndpoint() {
return VaultEndpoint.create("localhost", 8200);
}
问题已解决。现在我可以从 Java 客户端连接到本地 Vault。如果将来有人想要 运行 简单 Java 客户端 - Vault 演示,我会在此处粘贴代码。
控制器:
@RestController
@RequestMapping(Paths.ROOT)
@Api(value = Paths.ROOT, description = "Endpoint for core testing")
public class Controller {
@Autowired
CredentialsService credentialsService;
@GetMapping("/")
String home() throws URISyntaxException {
Credentials credentials = new Credentials("oliver", "exxeta123");
credentialsService.secureCredentials(credentials);
return credentialsService.accessCredentials().toString();
}
@GetMapping("/test")
public String test() throws IOException {
// http://127.0.0.1:8200/v1/sys/internal/ui/mounts/secret/mysecrets
VaultConfig vc = new VaultConfig();
String bearerToken = vc.clientAuthentication().login().getToken();
System.out.println(bearerToken);
// credentialsService.accessCredentials()
// Sending get request
//URL url = new URL("http://127.0.0.1:8200/v1/sys/internal/ui/mounts/secret/mysecrets");
// URL updated to match readme.adoc
URL url = new URL("http://127.0.0.1:8200/v1/kv/my-secret");
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestProperty("Authorization", "Bearer " + bearerToken);
conn.setRequestProperty("Content-Type", "application/json");
conn.setRequestMethod("GET");
BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
String output;
StringBuffer response = new StringBuffer();
while ((output = in.readLine()) != null) {
response.append(output);
}
in.close();
// printing result from response
return "Response: - " + response.toString();
}
@GetMapping(value = { "/add/{name}/{username}/{password}" })
public ResponseEntity<String> addKey(@PathVariable(value = "name", required = false, name = "name") String name,
@PathVariable(value = "username", required = false, name = "username") String username,
@PathVariable(value = "password", required = false, name = "password") String password) throws URISyntaxException {
Credentials credentials = new Credentials(username, password);
credentialsService.secureCredentials(name, credentials);
return new ResponseEntity<>("Add success: " + credentialsService.accessCredentials(name).getUsername(), HttpStatus.OK);
}
@GetMapping(value = {"/get", "/get/{name}"})
public ResponseEntity<Credentials> getKey(@PathVariable(value = "name", required = false, name = "name") String name) {
return new ResponseEntity<>(credentialsService.accessCredentials(name), HttpStatus.OK);
}
@GetMapping(value= {"/delete", "/delete/{name}"})
public String removeKey(@PathVariable(value = "name", required = false, name = "name") String name) {
return "Delete success: " + credentialsService.deleteCredentials(name);
}
}
服务:
@Service
public class CredentialsService {
private VaultTemplate vaultTemplate;
/**
* To Secure Credentials
*
* @param credentials
* @return VaultResponse
* @throws URISyntaxException
*/
public void secureCredentials(Credentials credentials) throws URISyntaxException {
//vaultTemplate.write("credentials/myapp", credentials);
initVaultTemplate();
vaultTemplate.write("kv/myapp", credentials);
}
public void secureCredentials(String storagePlace, Credentials credentials) {
initVaultTemplate();
vaultTemplate.write("kv/" + storagePlace, credentials);
}
/**
* To Retrieve Credentials
*
* @return Credentials
* @throws URISyntaxException
*/
public Credentials accessCredentials() throws URISyntaxException {
//VaultResponseSupport<Credentials> response = vaultTemplate.read("credentials/myapp", Credentials.class);
initVaultTemplate();
VaultResponseSupport<Credentials> response = vaultTemplate.read("kv/myapp", Credentials.class);
return response.getData();
// TODO special case when there are no values
}
/**
* @param nameOfsecrets key name
* @return if is presented or empty object
*/
public Credentials accessCredentials(String nameOfsecrets) {
initVaultTemplate();
VaultResponseSupport<Credentials> response = vaultTemplate.read("kv/" + nameOfsecrets, Credentials.class);
if (response != null) {
return response.getData();
} else {
return new Credentials();
}
}
public boolean deleteCredentials(String name) {
initVaultTemplate();
vaultTemplate.delete("kv/" + name);
return true;
}
}
private void initVaultTemplate() {
VaultEndpoint endpoint = new VaultEndpoint();
endpoint.setHost("localhost");
endpoint.setPort(8200);
endpoint.setScheme("http");
vaultTemplate = new VaultTemplate(endpoint, new VaultConfig().clientAuthentication());
}
保管库配置:
@Configuration
public class VaultConfig extends AbstractVaultConfiguration {
@Override
public ClientAuthentication clientAuthentication() {
return new TokenAuthentication("00000000-0000-0000-0000-000000000000");
}
@Override
public VaultEndpoint vaultEndpoint() {
return VaultEndpoint.create("localhost", 8200);
}
}
VaultEnvironmentConfig:
@Configuration
@PropertySource(value = { "vault-config.properties" })
@Import(value = EnvironmentVaultConfiguration.class)
public class VaultEnvironmentConfig {
}
主要class:
@SpringBootApplication
@EnableSwagger2
public class SpringVaultTutorial {
public static void main(String[] args) {
SpringApplication.run(SpringVaultTutorial.class, args);
}
//SWAGGER DOCUMENTATION BEANS
// default group contains all endpoints
@Bean
public Docket defaultApi() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any())//all
.build().apiInfo(apiInfo());
}
// Management group contains Spring Actuator endpoints
@Bean
public Docket swaggerAdminEndpoints() {
return new Docket(DocumentationType.SWAGGER_2)
.groupName(Paths.ROOT)
.apiInfo(apiInfo())
.select()
.paths(PathSelectors.regex("/v1/.*"))
.build()
.forCodeGeneration(true);
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("Vault Demo Application")
.description("Demo Application using vault")
.version("1.0")
.build();
}
}
保险库-config.properties:
vault.uri=http://127.0.0.1:8200
vault.token=00000000-0000-0000-0000-000000000000
application.properties:
server.port=8443
server.ssl.key-alias=selfsigned_localhost_sslserver
server.ssl.key-password=changeit
server.ssl.key-store=classpath:ssl-server.jks
server.ssl.key-store-provider=SUN
server.ssl.key-store-type=JKS
路径:
public class Paths {
public static final String ROOT = "/v1";
}
凭据:
public class Credentials {
private String username;
private String password;
public Credentials() {
}
public Credentials(String username, String password) {
this.username = username;
this.password = password;
}
public String getUsername() {
return username;
}
public String getPassword() {
return password;
}
@Override
public String toString() {
return "Credential [username=" + username + ", password=" + password + "]";
}
}
我在本地安装了 Vault。根据这个官方教程 https://learn.hashicorp.com/vault/
,我能够启动本地开发服务器和 write/read 进入 Vault kv 的一些秘密然后我想创建一些非常基本的 Java/Spring 引导演示客户端,它将连接到我的本地 Vault 开发服务器以 [=63=] 秘密。我阅读了 Baeldung 教程以获取灵感 https://www.baeldung.com/spring-vault。
这是我的金库-config.properties:
vault.uri=http://127.0.0.1:8200
vault.token=s.EXg6MQwUuB63Z7Xra4zybOut(服务器最近一次启动后生成的token)
然后服务 class:
@Service
public class CredentialsService {
@Autowired
private VaultTemplate vaultTemplate;
public void secureCredentials(Credentials credentials) throws URISyntaxException {
vaultTemplate.write("credentials/myapp", credentials);
}
public Credentials accessCredentials() throws URISyntaxException {
VaultResponseSupport<Credentials> response = vaultTemplate.read("credentials/myapp", Credentials.class);
return response.getData();
}
}
配置class:
@Configuration
public class VaultConfig extends AbstractVaultConfiguration {
@Override
public ClientAuthentication clientAuthentication() {
return new TokenAuthentication("s.EXg6MQwUuB63Z7Xra4zybOut");
}
@Override
public VaultEndpoint vaultEndpoint() {
return VaultEndpoint.create("host", 8200);
}
}
还有这个:
@Configuration
@PropertySource(value = { "vault-config.properties" })
@Import(value = EnvironmentVaultConfiguration.class)
public class VaultEnvironmentConfig {
}
一个域对象:
public class Credentials {
private String username;
private String password;
public Credentials() {
}
public Credentials(String username, String password) {
this.username = username;
this.password = password;
}
public String getUsername() {
return username;
}
public String getPassword() {
return password;
}
@Override
public String toString() {
return "Credential [username=" + username + ", password=" + password + "]";
}
}
最后是我的主要 Spring 启动 class:
@RestController
@ComponentScan
@SpringBootApplication
public class SpringVaultTutorial {
@Autowired
CredentialsService credentialsService;
@RequestMapping("/")
String home() throws URISyntaxException {
Credentials credentials = new Credentials("oliver","exxeta123");
credentialsService.secureCredentials(credentials);
return credentialsService.accessCredentials().getUsername().toString();
}
public static void main(String[] args) {
SpringApplication.run(SpringVaultTutorial.class, args);
}
}
Main class 应该写入 secret 然后立即读取它并打印用户名。但我收到此错误消息:
出现意外错误(类型=内部服务器错误,状态=500)。 I/O POST 请求“https://host:8200/v1/credentials/myapp”时出错:主机;嵌套异常是 java.net.UnknownHostException: host
有人知道哪里出了问题吗?
编辑: 根据 Arun 的建议,我遵循了本教程 https://drissamri.be/blog/java/enable-https-in-spring-boot/
我一直在尝试这两种方法。 1)修改application.properties:
server.port: 8443
server.ssl.key-store: keystore.p12
server.ssl.key-store-password: oliver
server.ssl.keyStoreType: PKCS12
server.ssl.keyAlias: tomcat
security.require-ssl=true
修改后,当我调用 https://localhost:8443 时,出现异常: javax.net.ssl.SSLException: 无法识别的 SSL 消息,明文连接? 在 sun.security.ssl.InputRecord.handleUnknownRecord(InputRecord.java:710) ~[na:1.8.0_121] 在 sun.security.ssl.InputRecord.read(InputRecord.java:527) ~[na:1.8.0_121] 在 sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:973) ~[na:1.8.0_121] 在 sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1375) ~[na:1.8.0_121] 在 sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1403) ~[na:1.8.0_121] 在 sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1387) ~[na:1.8.0_121]
2) 基于教程的第二种方法是添加 ConnectorConfig class:
@Configuration
public class ConnectorConfig {
@Bean
public ServletWebServerFactory servletContainer() {
TomcatServletWebServerFactory tomcat =
new TomcatServletWebServerFactory() {
@Override
protected void postProcessContext(Context context) {
SecurityConstraint securityConstraint = new SecurityConstraint();
securityConstraint.setUserConstraint("CONFIDENTIAL");
SecurityCollection collection = new SecurityCollection();
collection.addPattern("/*");
securityConstraint.addCollection(collection);
context.addConstraint(securityConstraint);
}
};
tomcat.addAdditionalTomcatConnectors(redirectConnector());
return tomcat;
}
private Connector redirectConnector() {
Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol");
connector.setScheme("http");
connector.setPort(8090);
connector.setSecure(false);
connector.setRedirectPort(8443);
return connector;
}
}
但是在调用 localhost:8090 将我重定向到 https://localhost:8443 之后,我得到了同样的错误:javax.net.ssl.SSLException:无法识别的 SSL 消息,纯文本连接? 在 sun.security.ssl.InputRecord.handleUnknownRecord(InputRecord.java:710) ~
现在的问题是:我是否还必须在 Vault 端配置一些关于证书的东西?或者您认为 Java 客户端可能存在证书问题?但我认为如果存在 Java 证书问题,启动期间就会抛出异常。
UnknownHostException 是由于名称 'host' 没有可用的服务器。您可以在主机文件映射中添加一个条目到本地主机。或尝试在创建保管库时更改主机名
@Override
public VaultEndpoint vaultEndpoint() {
return VaultEndpoint.create("localhost", 8200);
}
问题已解决。现在我可以从 Java 客户端连接到本地 Vault。如果将来有人想要 运行 简单 Java 客户端 - Vault 演示,我会在此处粘贴代码。
控制器:
@RestController
@RequestMapping(Paths.ROOT)
@Api(value = Paths.ROOT, description = "Endpoint for core testing")
public class Controller {
@Autowired
CredentialsService credentialsService;
@GetMapping("/")
String home() throws URISyntaxException {
Credentials credentials = new Credentials("oliver", "exxeta123");
credentialsService.secureCredentials(credentials);
return credentialsService.accessCredentials().toString();
}
@GetMapping("/test")
public String test() throws IOException {
// http://127.0.0.1:8200/v1/sys/internal/ui/mounts/secret/mysecrets
VaultConfig vc = new VaultConfig();
String bearerToken = vc.clientAuthentication().login().getToken();
System.out.println(bearerToken);
// credentialsService.accessCredentials()
// Sending get request
//URL url = new URL("http://127.0.0.1:8200/v1/sys/internal/ui/mounts/secret/mysecrets");
// URL updated to match readme.adoc
URL url = new URL("http://127.0.0.1:8200/v1/kv/my-secret");
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestProperty("Authorization", "Bearer " + bearerToken);
conn.setRequestProperty("Content-Type", "application/json");
conn.setRequestMethod("GET");
BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
String output;
StringBuffer response = new StringBuffer();
while ((output = in.readLine()) != null) {
response.append(output);
}
in.close();
// printing result from response
return "Response: - " + response.toString();
}
@GetMapping(value = { "/add/{name}/{username}/{password}" })
public ResponseEntity<String> addKey(@PathVariable(value = "name", required = false, name = "name") String name,
@PathVariable(value = "username", required = false, name = "username") String username,
@PathVariable(value = "password", required = false, name = "password") String password) throws URISyntaxException {
Credentials credentials = new Credentials(username, password);
credentialsService.secureCredentials(name, credentials);
return new ResponseEntity<>("Add success: " + credentialsService.accessCredentials(name).getUsername(), HttpStatus.OK);
}
@GetMapping(value = {"/get", "/get/{name}"})
public ResponseEntity<Credentials> getKey(@PathVariable(value = "name", required = false, name = "name") String name) {
return new ResponseEntity<>(credentialsService.accessCredentials(name), HttpStatus.OK);
}
@GetMapping(value= {"/delete", "/delete/{name}"})
public String removeKey(@PathVariable(value = "name", required = false, name = "name") String name) {
return "Delete success: " + credentialsService.deleteCredentials(name);
}
}
服务:
@Service
public class CredentialsService {
private VaultTemplate vaultTemplate;
/**
* To Secure Credentials
*
* @param credentials
* @return VaultResponse
* @throws URISyntaxException
*/
public void secureCredentials(Credentials credentials) throws URISyntaxException {
//vaultTemplate.write("credentials/myapp", credentials);
initVaultTemplate();
vaultTemplate.write("kv/myapp", credentials);
}
public void secureCredentials(String storagePlace, Credentials credentials) {
initVaultTemplate();
vaultTemplate.write("kv/" + storagePlace, credentials);
}
/**
* To Retrieve Credentials
*
* @return Credentials
* @throws URISyntaxException
*/
public Credentials accessCredentials() throws URISyntaxException {
//VaultResponseSupport<Credentials> response = vaultTemplate.read("credentials/myapp", Credentials.class);
initVaultTemplate();
VaultResponseSupport<Credentials> response = vaultTemplate.read("kv/myapp", Credentials.class);
return response.getData();
// TODO special case when there are no values
}
/**
* @param nameOfsecrets key name
* @return if is presented or empty object
*/
public Credentials accessCredentials(String nameOfsecrets) {
initVaultTemplate();
VaultResponseSupport<Credentials> response = vaultTemplate.read("kv/" + nameOfsecrets, Credentials.class);
if (response != null) {
return response.getData();
} else {
return new Credentials();
}
}
public boolean deleteCredentials(String name) {
initVaultTemplate();
vaultTemplate.delete("kv/" + name);
return true;
}
}
private void initVaultTemplate() {
VaultEndpoint endpoint = new VaultEndpoint();
endpoint.setHost("localhost");
endpoint.setPort(8200);
endpoint.setScheme("http");
vaultTemplate = new VaultTemplate(endpoint, new VaultConfig().clientAuthentication());
}
保管库配置:
@Configuration
public class VaultConfig extends AbstractVaultConfiguration {
@Override
public ClientAuthentication clientAuthentication() {
return new TokenAuthentication("00000000-0000-0000-0000-000000000000");
}
@Override
public VaultEndpoint vaultEndpoint() {
return VaultEndpoint.create("localhost", 8200);
}
}
VaultEnvironmentConfig:
@Configuration
@PropertySource(value = { "vault-config.properties" })
@Import(value = EnvironmentVaultConfiguration.class)
public class VaultEnvironmentConfig {
}
主要class:
@SpringBootApplication
@EnableSwagger2
public class SpringVaultTutorial {
public static void main(String[] args) {
SpringApplication.run(SpringVaultTutorial.class, args);
}
//SWAGGER DOCUMENTATION BEANS
// default group contains all endpoints
@Bean
public Docket defaultApi() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any())//all
.build().apiInfo(apiInfo());
}
// Management group contains Spring Actuator endpoints
@Bean
public Docket swaggerAdminEndpoints() {
return new Docket(DocumentationType.SWAGGER_2)
.groupName(Paths.ROOT)
.apiInfo(apiInfo())
.select()
.paths(PathSelectors.regex("/v1/.*"))
.build()
.forCodeGeneration(true);
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("Vault Demo Application")
.description("Demo Application using vault")
.version("1.0")
.build();
}
}
保险库-config.properties:
vault.uri=http://127.0.0.1:8200 vault.token=00000000-0000-0000-0000-000000000000
application.properties:
server.port=8443
server.ssl.key-alias=selfsigned_localhost_sslserver
server.ssl.key-password=changeit
server.ssl.key-store=classpath:ssl-server.jks
server.ssl.key-store-provider=SUN
server.ssl.key-store-type=JKS
路径:
public class Paths {
public static final String ROOT = "/v1";
}
凭据:
public class Credentials {
private String username;
private String password;
public Credentials() {
}
public Credentials(String username, String password) {
this.username = username;
this.password = password;
}
public String getUsername() {
return username;
}
public String getPassword() {
return password;
}
@Override
public String toString() {
return "Credential [username=" + username + ", password=" + password + "]";
}
}