We are using okta for authentication in our Spring boot project. We have successfully done authentication using java application as (refer - https://developer.okta.com/blog/2017/03/21/spring-boot-oauth)
Now what I am trying to do is move the okta clientId and secret property to vault.
Bootstrap for vault
spring.cloud.vault:
host: localhost
port: 8200
scheme: http
token: 00000000-0000-0000-0000-000000000000
Now please have a look at the below 2 cases for application properties
Case 1 : properties used using #Value works
am.clientId=${account.clientId}
am.issuer=${account.issuer}
used as
#Value("${am.clientId}")
private String clientId;
#Value("${am.issuer}")
private String clientSecret;
Case 2 : used as spring properties does not work
I use the same properties for spring oAuth and it fails
okta.oauth2.clientId=${account.clientId}
okta.oauth2.issuer=${account.issuer}
Exception log
java.lang.IllegalArgumentException: Could not resolve placeholder 'account.clientId' in value "${account.clientId}"
account-web_1 | at org.springframework.util.PropertyPlaceholderHelper.parseStringValue(PropertyPlaceholderHelper.java:172) ~[spring-core-5.0.5.RELEASE.jar!/:5.0.5.RELEASE]
account-web_1 | at org.springframework.util.PropertyPlaceholderHelper.replacePlaceholders(PropertyPlaceholderHelper.java:124) ~[spring-core-5.0.5.RELEASE.jar!/:5.0.5.RELEASE]
account-web_1 | at org.springframework.core.env.AbstractPropertyResolver.doResolvePlaceholders(AbstractPropertyResolver.java:237) ~[spring-core-5.0.5.RELEASE.jar!/:5.0.5.RELEASE]
account-web_1 | at org.springframework.core.env.AbstractPropertyResolver.resolveRequiredPlaceholders(AbstractPropertyResolver.java:211) ~[spring-core-5.0.5.RELEASE.jar!/:5.0.5.RELEASE]
[Update]
So debugging the spring code I realised that the problem is only with property
okta.oauth2.issuer
Github has one issue regarding same, but the okta spring boot satarter 0.6.0 version still has issue.
Have you tried setting the okta.oauth2.* properties in your cloud config provider? I usually test Cloud Config using a file repo, but from the client's perspective it should be the same. (I know this doesn't fully answer your question, but just want to make sure that works before continuing)
So I have got one sollution to this one. Created a bean "oktaOAuth2Properties" to overrid the spring boot default bean and get the value from vault using the #Value annotation. the code looks like below. This works for me
#Value("${okta.clientId}")
private String clientId;
#Value("${okta.issuer}")
private String issuer;
#Bean
public OktaOAuth2Properties oktaOAuth2Properties() {
OktaOAuth2Properties properties = new OktaOAuth2Properties();
properties.setClientId(clientId);
properties.setIssuer(issuer);
return properties;
}
Related
This is my very first question so I apologize if it is not specific enough (please be gentle haha) and I have already gone through documentation, but I am new to the field and none of it helped.
I am making a simple project that uses Java 18 and Spring Boot to make a call to an external API call and I want to hide my API key that I use for obvious reasons.
As of right now, I have an application-dev.properties file in my resources directory with the following (I am making up the actual key for security) and I have application-dev.properties in my .gitignore file so it doesn't get committed:
api-key=someText
And I am trying to use that value in my controller like so:
#RestController
public class ImageController {
#Value("${api-key}")
private String API_KEY;
#RequestMapping(value = "/image")
public List<String> getImages(#RequestParam(defaultValue = "4") String request) {
String url = "https://api.nasa.gov/planetary/apod?" + API_KEY + request;
RestTemplate restTemplate = new RestTemplate();
The error I am receiving is this:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'imageController': Injection of autowired dependencies failed; nested exception is java.lang.IllegalArgumentException: Could not resolve placeholder 'api-key' in value "${api-key}"
Thanks in advance!
As mentioned in the other answer,
the name of the application-dev.properties file requires that
you activate the "dev" profile or Spring Boot will never read it and the
values therein will not be available to your #Value annotation.
Here is a link to a Baeldung article that discusses Spring Boot profiles:
Spring Boot Profiles at Baeldung
(I am not associated with Baeldung,
I just like much of their stuff).
If you are not planning to use profiles in your application,
change the name of the properties file to "application.properties".
As you mentioned you are using application-dev.properties therefore you have to do profiling correctly. In your application.properties add spring.profiles.active=dev to activate dev profile.
I'm trying to connect to a postgresql instance on google cloud from a spring boot application, using beans.
I created a bean:
#Bean
#Primary
#ConfigurationProperties(prefix = "spring.datasource")
fun createConnectionPool(): DataSource {
val config = HikariConfig()
config.jdbcUrl = String.format("jdbc:postgresql:///%s", DB_NAME)
config.username = DB_USER
config.password = DB_PASS
config.addDataSourceProperty("cloudSqlInstance", INSTANCE_CONNECTION_NAME)
return HikariDataSource(config)
}
and the application.yml file looks like this:
spring:
jp:
features:
hibernate:
jdbc:
lobe:
non_contextual_creation: true
clouds:
gcp:
projectId: my-project-id
SQL:
instance-connection-name: "my-instance"
databaseName: "my-database-name"
but I am getting this error:
could not obtain connection to query metadata","stack_trace":"o.p.u.PSQLException: The server requested password-based authentication, but no password was provided by plugin null\n\t
but I gave it the password :(
I want to mention that I cannot give the password in application.yml
(Because the password comes from an external source and I cannot access it in the application yml file)
What am I doing wrong? Any idea is welcome. thanks
I have a spring boot project with cloud config server.I have enabled cloud bus with kafka and added required actuator endpoints to live reload properties on both server and client.
cloud:
bus:
enabled: true
management:
endpoints:
web:
exposure:
include: refresh, bus-refresh, beans, env
Issue is when I am using #value annotation to read property value. I am not getting updating value. (#value only get updated when I apply #RefreshScope,But I cannot use it because I have ton of client services and will to add #RefreshScope on all beans where properties are being used)
#Value("${message.testValue}")
private String info;
private String readValue() {
return info;
}
But when I read using Environment, I am getting updated value after refresh
private String readValue() {
return this.environment.getProperty("message.testValue");
}
What changes I need to implement to get updated value using #Value annotation withou #RefreshScope? Or there is something which I am missing in implementation.
I'm currently developing multi-tenant application using spring and using oauth. Every tenant will have different database. If my url is tenant1.xxx.com, then it will use tenant1 database, etc.
My request already successfully routed using AbstractRoutingDataSource but not the authentication. So when I ask an access token, it still using the default datasource. I think my problem is in oauth2 configuration that set datasource like TokenStore, etc.
#Autowired
private DataSource dataSource;
#Bean
public TokenStore tokenStore() {
return new JdbcTokenStore(dataSource);
}
Is there anyway that token store can choose datasource according to current tenant?
Let me try to give solutions
1) If you check to code inside the JdbcTokenStore then we will find that it's using inside the JdbcTempaltes so JPA or Hibernate routing will not work for that in that case a part of the solution you have to implements org.springframework.security.oauth2.provider.token.TokenStore with JPA implementation.
2) If you do not like to do a solution No 1 then you can also implement AbstractRoutingDataSource and while creating TokenStore passed the routing data source.
I specified Spring properties inside the application.properties file. How can i populate those properties from the environment variables?
Here is what I tried, but it doesn't seem to work:
application.properties
spring.datasource.url=jdbc:postgresql://#{ systemProperties['DATABASE_HOST']}:5432/dbname
spring.datasource.username = postgres
spring.datasource.password = postgres
You can refer to environment properties in the same way you refer to Spring properties using ${...} syntax.
In your case:
spring.datasource.url=jdbc:postgresql://${DATABASE_HOST}:5432/dbname
Out of the box, as you know, spring-boot expects its Datasource details to be using a specific set of variable names. Being spring of course you can rework this if you need by a few methods:
1/ If the need to use variables from the environment comes from deployment to a cloud service such as Cloud Foundry or Horuku, there is spring-boot-starter-cloud-connector which handles allot of the plumbing out of the box. A good read is the (Binding to Data Services with Spring Boot in Cloud Foundry article and the Deploying to the cloud docs which walks you thru this
2/ Instead of relying on Spring-Boot's own auto-magical wiring mechanism, you can create a custom configuration bean to override how the DataSource information is populated. A good read explaining the annotations involved can be found here: Spring Java Config Documentation - #Bean Configuration JavaDOC. Based on your example above, here is what I spat out:
#Configuration
public class MyDataSourceConfig {
#Bean
#Primary
public DataSource getDataSource() {
String url = "jdbc:postgresql://" + System.getenv("DATABASE_HOST") + ":5432/dbname";
String username = "postgres";
String password = "postgres";
String driverClassName = "org.postgresql.Driver";
/*
* Create the datasource and return it
*
* You could create the specific DS
* implementation (ie: org.postgresql.ds.PGPoolingDataSource)
* or ask Spring's DataSourceBuilder to autoconfigure it for you,
* whichever works best in your eyes
*/
return DataSourceBuilder
.create()
.url( url )
.username( username )
.password( password )
.driverClassName( driverClassName )
.build();
}
}
Just remember that in spring, you can always override allot of the default behaviours with a little bit of digging!
Hope this helps!
You don't have to. When Spring Boot initializes its environment, it pulls stuff from both the application.properties file and any system-level variables and combines them together. The full list of locations where Spring takes them from is here, specifically points 9) and 10).