Spring Data. Change DB user credentials at runtime - java

There is Spring Boot Aplication. For forking with DB I use Spring Data (JPA + Repository).
Is there way to change DB user-credentials for datasource per API user-request? I mean each MVC request must do his own work with exactly his DB user-credentials.
I've read info about AbstractRoutingDataSource. But there is limitation: there isn't possibility to add datasource at runtime in DataSourceMap.
In fact I want to use apache PerUserPoolDataSource and change DB user-credentials (using Spring Security Context to get user information)

I've found solution. First of all need to define custom datasource configuration.
#Bean
public UserCredentialsDataSourceAdapter dataSource() throws ClassNotFoundException {
final UserCredentialsDataSourceAdapter dataSourceAdapter = new UserCredentialsDataSourceAdapter();
dataSourceAdapter.setTargetDataSource(perUserPoolDataSource());
return dataSourceAdapter;
}
private PerUserPoolDataSource perUserPoolDataSource() throws ClassNotFoundException {
DriverAdapterCPDS driverAdapter = new DriverAdapterCPDS();
driverAdapter.setDriver(properties.getDriverClassName());
driverAdapter.setUrl(properties.getUrl());
driverAdapter.setUser(properties.getUsername());//default
driverAdapter.setPassword(properties.getPassword());//default
driverAdapter.setAccessToUnderlyingConnectionAllowed(true);
PerUserPoolDataSource dataSource = new PerUserPoolDataSource();
dataSource.setConnectionPoolDataSource(driverAdapter);
return dataSource;
}
As you see - there is base datasource from Apache: PerUserPoolDataSource.
Also as main datasource I will use UserCredentialsDataSourceAdapter.
Then need to write custom filter, which will change user credentials for successefully authenticated users:
dataSourceAdapter.setCredentialsForCurrentThread(user.getUsername(), user.getPassword());
This solution works for blocked paradygm (not reactive). One thread with credentials for specific user will do all queries.

Related

How does Spring deal with #Autowired on fields that required a parameter? In this case, JdbcTemplate with DataSource

I have a SpringBoot app that I want to connect to my MySQL database and I want to connect it with JDBC (by itself, not using JPA). And from what I have seen on articles, one way to achieve this is with JdbcTemplate and DataSource objects).
Now I have a RestController where I call my database, "CoffeeShop" which has me with the following class/code:
#RestController
public class MenuController {
#Autowired
private DataSource dataSource;
#Autowired
private JdbcTemplate jdbcTemplate;
private String menuQuery = "SELECT * FROM menu";
#CrossOrigin(origins = "http://localhost:4200")
#GetMapping(path="/menu")
public String getMenu(){
jdbcTemplate.query(menuQuery, (rs, rowNum) -> new Menu(rs.getString("name"))).forEach(
customer-> System.out.println(customer.getName()));
return "worked";
}
private List<Menu> organizeMenu() {
return null;
}
}
If my understanding is correct, I expect that dataSource will be able to see in my application.properties file the following contents when being compiled and then Spring figures out that jdbcTemplate requires it?:
spring.jpa.hibernate.ddl-auto=none
spring.datasource.driverclassname = com.mysql.jdbc.Driver
hibernate.dialect=org.hibernate.dialect.MySQL5Dialect
spring.datasource.url=jdbc:mysql://${MYSQL_HOST:localhost}:3306/CoffeeShop?useUnicode=true&useLegacyDatetimeCode=false&serverTimezone=UTC
spring.datasource.username=springuser
spring.datasource.password=ThePassword
To my surprise this worked, my code queried the DB and logged the correct output. But I'm not sure how this worked since jdbcTemplate required dataSource?
See the Spring Boot Getting Started | Accessing Relational Data using JDBC with Spring guide, which says:
Spring Boot supports H2 (an in-memory relational database engine) and automatically creates a connection. Because we use spring-jdbc, Spring Boot automatically creates a JdbcTemplate. The #Autowired JdbcTemplate field automatically loads it and makes it available.
This is what you get with the auto-configuration provided by Spring Boot: Fully functional JdbcTemplate automatically created and configured from the application.properties file.
FYI: The JdbcTemplate is already configured to use the DataSource, so you don't need to auto-wire the DataSource. As you can see in your own code, the dataSource field isn't used anywhere, so you should remove it.
It is actually the DataSource that is auto-configured from the application.properties file.

How can I run Spring with MongoDB disabled, but still installed?

I was reading through this Git issue:
https://github.com/spring-projects/spring-boot/issues/7589
with regards to Java Spring boot and am trying to figure out a way to bypass the crash upon startup.
The short version is that If you include the code for creating the mongo client:
#Bean
public MongoOperations mongoOperations() {
try {
//This runs an operation which uses my credentials to login to the db
return new MongoTemplate(mongoDbFactory());
} catch (Exception e){
e.printStackTrace();
return null;
}
}
and the MongoDB is running, it will connect and not have any problems, but if the MongoDB is not running, Spring will retry and after failing again, will crash and stop all startup sequences.
My question is this: is there a way to bypass this initial crash / check against the DB being up and running other than commenting out all code referencing it? Can I catch an exception somewhere low-level and let it pass through?
If your application behaves in such a way that MongoDB is optional, you have several options.
If you are migrating an existing application, the easiest from a start would be to exclude the auto-configuration and create the infrastructure yourself. Not in the way you've indicated as returning null from a #Bean method is quite nasty. Rather you could have some service that could lazily create the client and you could update your optional usages of MongoDB to go through that service. The service would be created regardless but would only create the underlying infrastructure if necessary.
The other option is to use a profile. If the main use case is that MongoDB is available then create a application-nomongo.properties (something like that) where you would exclude the auto-configuration using the spring.autoconfigure.exclude property. When the application starts without mongo, you can enable the nomongo profile and the auto-configuration will backoff. When it's not enabled, the Mongo bean will be created by Spring Boot.
I had the same issue. I want my application to be able to run offline as well, queue stuff and from time to time flush to database. This means it needs to be able to launch offline and connect whenever internet is available. Ofc, Spring crashed my app when it did not succeed in connecting.
What worked for me was this:
#Configuration
#EnableJpaRepositories(bootstrapMode = BootstrapMode.LAZY)
public class DataSourceConfig {
#Bean(destroyMethod = "close")
public DataSource dataSource() {
HikariConfig hikariConfig = new HikariConfig();
hikariConfig.setDriverClassName("xxx");
hikariConfig.setJdbcUrl("xxx");
hikariConfig.setUsername("xxx");
hikariConfig.setPassword("xxx");
hikariConfig.setConnectionTestQuery("SELECT 1");
hikariConfig.setPoolName("springHikariCP");
hikariConfig.setInitializationFailTimeout(-1);
hikariConfig.addDataSourceProperty("dataSource.cachePrepStmts", "true");
hikariConfig.addDataSourceProperty("dataSource.prepStmtCacheSize", "250");
hikariConfig.addDataSourceProperty("dataSource.prepStmtCacheSqlLimit", "2048");
hikariConfig.addDataSourceProperty("dataSource.useServerPrepStmts", "true");
HikariDataSource dataSource = new HikariDataSource(hikariConfig);
return dataSource;
}
#Bean
public EntityManager entityManager() {
return entityManagerFactory().getObject().createEntityManager();
}
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(dataSource());
em.setPackagesToScan("blabla");
em.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
em.setJpaProperties(additionalProperties());
return em;
}
private Properties additionalProperties() {
Properties properties = new Properties();
properties.setProperty("hibernate.dialect", "org.hibernate.dialect.MySQL55Dialect");
return properties;
}
}
Also, place this in your App class
#SpringBootApplication(exclude = HibernateJpaAutoConfiguration.class)
application.yml looks like this:
#Spring properties
spring:
jpa:
hibernate:
ddl-auto: update
datasource:
url: "xxx"
username: xxx
password: xxx
driver-class-name: com.mysql.cj.jdbc.Driver
continue-on-error: true
hikari:
connection-timeout: 2000
initialization-fail-timeout: -1
auto-commit: true
This works for me, hopefully it will work for you guys as well, I saw many posts around here searching for a solution to this. Good luck!

How to kill Hibernates database connections in Spring boot 1.5.2?

The problem is simple when I use Spring boot 1.5.2 with Hibernate to connect to a datasource with configued like this:
#Bean
public DataSource dataSource() {
DataSourceBuilder = datasourceBuilder = DataSourceBuilder.create();
dataSourceBuilder.url(DATASOURCE_URL);
dataSourceBuilder.username(DATASOURCE_USERNAME);
dataSourceBuilder.password(DATASOURCE_PASSWORD);
return dataSourceBuilder.build();
}
and this is a Command line application, so when the application is almost done, I just want to close the Hibernate connections to the database and rename the database with JDBC. However, I don't know how to do this in Spring boot, any idea?
I tried to inject the DataSource object to a class to rename database but it cannot close the connections to database.
#Autowired
private DataSource dataSource;
public void closeConnection() {
dataSource.close();
}
with error
org.postgresql.util.PSQLException: ERROR: database "DATABASE" is being accessed by other users
Detail: There are 10 other sessions using the database.
You can use pg_terminate_backend() to kill a connection. You have to be superuser to use this function. This works on all operating systems the same.
refer this stackoverflow link :
https://stackoverflow.com/a/5109190/7801800

java spring connect to mysql database (not JNDI pool or system datasource!)

I have a java spring application that uses it's postgres db, it's no prob. But also I have a quartz job that runs every hour and syncing data from other mysql DS with postgres. The issue is JDBC connection string is dynamic and is being changed, I can't just specify it on systemstartup, etc. How to do that gracefully? I mean take jdbc connection string, connect to db and retrieve data from there? I also use spring-data there.
One possible solution is to use spring batch #JobScope beans and have your quartz job launch the spring batch job.
You can configure a datasource to have #JobScope and this would then make spring create that datasource each time the job is executed. You can then use job parameters or other variables to create the specific datasource that you want. An example of a configuration class that creates a datasource like this is below...
#Configuration
public class JobDatabaseConfiguration {
#Bean
#JobScope
public DataSource jobDataSource() throws IOException {
// set these from job parameters...
String jdbcUrl = "<your jdbc url>";
String driver = "<your driver>";
String user = "user";
String password = "password";
return DataSourceBuilder.create()
.driverClassName(driver)
.url(jdbcUrl)
.username(user)
.password(password)
build();
}
}

Spring Boot with session-based data source

I've been tearing my hair out with what should be a pretty common use case for a web application. I have a Spring-Boot application which uses REST Repositories, JPA, etc. The problem is that I have two data sources:
Embedded H2 data source containing user authentication information
MySQL data source for actual data which is specific to the authenticated user
Because the second data source is specific to the authenticated user, I'm attempting to use AbstractRoutingDataSource to route to the correct data source according to Principal user after authentication.
What's absolutely driving me crazy is that Spring-Boot is fighting me tooth and nail to instantiate this data source at startup. I've tried everything I can think of, including Lazy and Scope annotations. If I use Session scope, the application throws an error about no session existing at startup. #Lazy doesn't appear to help at all. No matter what annotations I use, the database is instantiated at startup by Spring Boot and doesn't find any lookup key which essentially crashes the entire application.
The other problem is that the Rest Repository API has IMO a terrible means of specifying the actual data source to be used. If you have multiple data sources with Spring Boot, you have to juggle Qualifier annotations which is a runtime debugging nightmare.
Any advice would be very much appreciated.
Your problem is with the authentication manager configuration. All the samples and guides set this up in a GlobalAuthenticationConfigurerAdapter, e.g. it would look like this as an inner class of your SimpleEmbeddedSecurityConfiguration:
#Configuration
public static class AuthenticationConfiguration extends GlobalAuthenticationConfigurerAdapter
{
#Bean(name = Global.AUTHENTICATION_DATA_QUALIFIER + "DataSource")
public DataSource dataSource()
{
return new EmbeddedDatabaseBuilder().setName("authdb").setType(EmbeddedDatabaseType.H2).addScripts("security/schema.sql", "security/data.sql").build();
}
#Override
public void init(AuthenticationManagerBuilder auth) throws Exception
{
auth.jdbcAuthentication().dataSource(dataSource()).passwordEncoder(passwordEncoder());
}
}
If you don't use GlobalAuthenticationConfigurerAdapter then the DataSource gets picked up by Spring Data REST during the creation of the Security filters (before the #Primary DataSource bean has even been registered) and the whole JPA initialization starts super early (bad idea).
UPDATE: the authentication manager is not the only problem. If you need to have a session-scoped #Primary DataSource (pretty unusual I'd say), you need to switch off everything that wants access to the database on startup (Hibernate and Spring Boot in various places). Example:
spring.datasource.initialize: false
spring.jpa.hibernate.ddlAuto: none
spring.jpa.properties.hibernate.temp.use_jdbc_metadata_defaults: false
spring.jpa.properties.hibernate.dialect: H2
FURTHER UPDATE: if you're using the Actuator it also wants to use the primary data source on startup for a health indicator. You can override that by prividing a bean of the same type, e.g.
#Bean
#Scope(value="session", proxyMode=ScopedProxyMode.TARGET_CLASS)
#Lazy
public DataSourcePublicMetrics dataSourcePublicMetrics() {
return new DataSourcePublicMetrics();
}
P.S. I believe the GlobalAuthenticationConfigurerAdapter might not be necessary in Spring Boot 1.2.2, but it is in 1.2.1 or 1.1.10.

Categories