Update Spring ACL Datasource in MethodSecurityConfiguration for Multi Tenancy Use Case - java

Use Case
I want to implement Spring Security ACL in Spring Boot multi tenancy system.
Approach
In order to achieve this, I need to change datasource bean which is created on application bootstrap. I need to connect to datasource after the tenant user logs in.
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class MethodSecurityConfiguration extends GlobalMethodSecurityConfiguration {
[...]
// data source
#Bean
#ConfigurationProperties(prefix = "spring.datasource")
public DataSource dataSource() {
return DataSourceBuilder.create().build();
}
}
Prerequisites:
ACL is working fine in single tenant environment.
Multi tenancy is working also.
Question:
How can I refresh datasource in MethodSecurityConfiguration after system startup?
Thanks for your help!

Related

Java Spring boot multiple databases, repository is using the wrong database

(help to create multiple databases in spring boot in my answer)
I want to make a spring boot application for different games. The data of each game is placed in a separate database. I have been trying to connect a secondary database to my existing application, but the controller/repository keeps using the primary database.
This is what the controller for the second database looks like:
#RestController
#RequestMapping("/valorant")
#CrossOrigin
public class UserController {
#Autowired
private UserRepository userRepository;
#Autowired
#Qualifier("valorantDataSource")
private DataSource valorantDataSource;
#GetMapping("/users")
public Iterable<User> getUsers() {
return userRepository.findAll();
}
}
This is the repository that should take the data from the second database:
#Repository
#Qualifier("valorantDataSource")
public interface UserRepository extends JpaRepository<User, Long> {
}
I have defined the datasources in a different file. (Adding .url(), .username() and .password() to the datasourcebuilder did not help)
#Configuration
public class DatasourceConfig {
#Primary
#Bean(name = "apexDataSource")
#ConfigurationProperties(prefix = "spring.datasource")
public DataSource dataSource() {
return DataSourceBuilder.create().build();
}
#Bean(name = "valorantDataSource")
#ConfigurationProperties(prefix = "spring.second-datasource")
public DataSource valorantDataSource() {
return DataSourceBuilder.create()
.build();
}
}
# First database
spring.datasource.jdbc-url=jdbc:mysql://localhost:3306/apex
spring.datasource.username=root
spring.datasource.password=
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
# Second database
spring.second-datasource.jdbc-url=jdbc:mysql://localhost:3306/valorant
spring.second-datasource.username=root
spring.second-datasource.password=
spring.second-datasource.driver-class-name=com.mysql.cj.jdbc.Driver
When I used postman to go to http://localhost:8080/valorant/users, i got the error:
java.sql.SQLSyntaxErrorException: Table 'apex.user' doesn't exist
So it seems like the application is trying to look in the wrong database.
Thanks in advance.
You should not use #Primary annotation in this case.
When you mark bean with #Primary, it will be selected for dependency injection
Marking your beans with #Qualifier would be enough to select concrete bean.
I ended up using an existing project. I cloned this repo, changed some names and values and it was ready to use in my own project.

Spring Boot Multiple Datasources, specify Datasource on Repository

I´m working on some legacy code, where we have 2 different datasources for our application.
There is one class that defines the configuration for both, and I don't want to separate them into two classes because things will probably break. This is the configuration class
#Configuration
public class DataSourceConfig {
#Bean(name="dataSource")
#Primary
#ConfigurationProperties(prefix = "spring.datasource")
public DataSource dataSource() {
return DataSourceBuilder.create().build();
}
#Bean(name="merchantsDataSource")
#ConfigurationProperties(prefix = "merchants.datasource")
public DataSource merchantsDataSource() {
return DataSourceBuilder.create().build();
}
}
Being structured this way, I don't think I can use #EnableJpaRepositories as suggested in many places to be able to define Repositories from different Data Sources.
So my question is: is there a way that I can create a Repository for the non-primary Data Source, without having to refactor all legacy code to use new classes?

How can I use different variable names for common Spring Boot Application Properties, when the app never directly calls these variables?

I've created a pretty standard Spring Boot 2.0 app with Services and Repositories to access a database.
I had to set the standard Spring Boot application properties to get it to work, such as:
spring.datasource.url, spring.jpa.database, etc.
However, in order to prevent my properties from overwriting other properties in similar apps hosted in the same place, I need to rename these properties, such as:
myApp.spring.datasource.url, myApp.spring.jpa.database, etc.
Some of these properties will be set by environmental variables instead of the application.properties file.
However, I can't see any way to override those variables in my app.
The standard approach is to use #Value to configure those variables. However, the Spring Boot 2.0 setup for services looks up all these properties "behind the scenes," so that doesn't appear to be an option here.
Is there any way to configure my app to read all those myApp.common.property.name properties and treat them as common.property.name?
Thank you.
Yes, the standard way is to use #Value. But thw work doesn't stop there, you need to create DataSource and EntityManager with these values.
Springboot will create DataSource, Entitymanager and some other components bey looking into default properties(spring.xxx) from the file(hence Spring boot is opinionated). But when you change these names to non default values, then you need to create these components / beans yourself.
Instead of using #Value you could also use #Configurationproperties. #Value also works but you might need to declare like 6 or 7 values with #Value. If you wish to use #ConfigurationProperties, make sure you have #EnableConfigurationProperties annotation added somewhere in your project.
Here is a code snippet. You need to tune it to your project
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(
entityManagerFactoryRef = "entityManagerFactory",
basePackages = { "com.xxx.yyy.repo" }
)
public class SomeDbConfig {
#Primary // Use this if you have multiple datasources or else no use
#Bean(name = "dataSource")
#ConfigurationProperties(prefix = "myApp.spring.datasource")
public DataSource dataSource() {
return DataSourceBuilder.create().build();
}
#Primary
#Bean(name = "entityManagerFactory")
public LocalContainerEntityManagerFactoryBean
entityManagerFactory(
EntityManagerFactoryBuilder builder,
#Qualifier("dataSource") DataSource dataSource
) {
return builder
.dataSource(dataSource)
.packages("com.xxx.yyy.domain")
.persistenceUnit("somename")
.build();
}
#Primary
#Bean(name = "transactionManager")
public PlatformTransactionManager transactionManager(
#Qualifier("entityManagerFactory") EntityManagerFactory
entityManagerFactory
) {
return new JpaTransactionManager(entityManagerFactory);
}
}

Change Spring config connection in runtime

I need to change my connection with the Database in runtime. For Example, if parameter requested is BD1, connect on database 1, if BD2, connect in database 2, etc.
I am using spring boot. What is the best way for this.
I have this #Configuration, but not know to say my repository how to use.
#Configuration
public class DataSourceConfiguration {
#Bean(name = "ccteste")
#ConfigurationProperties("spring.ciclocairu.teste.datasource")
#Primary
public DataSource ciclocairuTeste() {
return DataSourceBuilder.create().build();
}
#Bean(name = "ccprod")
#ConfigurationProperties("spring.ciclocairu.prod.datasource")
public DataSource ciclocairuProd() {
return DataSourceBuilder.create().build();
}
#Bean(name = "tmccteste")
#Autowired
#Primary
DataSourceTransactionManager transactionManagerCicloCairuTeste(#Qualifier("ccteste") DataSource datasource) {
DataSourceTransactionManager txm = new DataSourceTransactionManager(datasource);
return txm;
}
#Bean(name = "tmccprod")
#Autowired
#Primary
DataSourceTransactionManager transactionManagerCicloCairuProd(#Qualifier("ccprod") DataSource datasource) {
DataSourceTransactionManager txm = new DataSourceTransactionManager(datasource);
return txm;
}
}
Looks like you are looking for some data source routing. Spring has AbstractRoutingDataSource for run-time detection what data source should be used.
Abstract DataSource implementation that routes getConnection() calls
to one of various target DataSources based on a lookup key.
Also you can set default datasource by setDefaultTargetDataSource method.
It works in such way: you put data sources that you need in a map in AbstractRoutingDataSource at the bean configuration stage, and when you need to use a specific data source you put the key for this source into the context that linked to the router. This ds-key is linked to the current thread.
Here are examples : Dynamic DataSource Routing with Spring and Spring DataSource Routing
Might take a look at my answer at Manage transactions with multiple datasource, entity managers for same application code
or my blog post: Multi-tenant applications using Spring Boot, JPA, Hibernate and Postgres

Configuring Springboot to work with 2 different databases

I am just learning how to use springboot as a java backend framework and I currently have applications.properties configured to use 1 database.
I am thinking of adding an additional database to store different information instead of saving everything on a single database so I was wondering how (if possible) can i do that?
My application.properties file contains data like this:
spring.datasource.driverClassName=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://database:3306...
Any ideas?
You can create two datasources, one bean of them mark as #Primary
#Bean
#ConfigurationProperties(prefix = "datasource.mysql")
public DataSource mysqlDataSource() {
return DataSourceBuilder.create().build();
}
#ConfigurationProperties(prefix = "datasource.postgres")
#Bean
#Primary
public DataSource postgresDataSource() {
return DataSourceBuilder.create().
build();
}
Your application.properties should looks like this:
datasource.mysql.url=jdbc:mysql://localhost:3306/mysql_demo
datasource.mysql.username=root
datasource.mysql.password=root
datasource.mysql.driverClassName=com.mysql.jdbc.Driver
datasource.postgres.url=jdbc:postgresql://localhost:5432/postgres_demo
datasource.postgres.username=postgres
datasource.postgres.password=postgres
datasource.postgres.driverClassName=org.postgresql.Driver

Categories