I'm playing around with Spring Boot and the reactive jdbc driver called r2dbc. In my main application I'm using Postgres as a database and now I want to the use h2 for the tests. And the Flyway migration is working with the setup but when the Spring application is able to insert records.
Here is my setup and code
#SpringBootTest
class CustomerRepositoryTest {
#Autowired
CustomerRepository repository;
#Test
void insertToDatabase() {
repository.saveAll(List.of(new Customer("Jack", "Bauer"),
new Customer("Chloe", "O'Brian"),
new Customer("Kim", "Bauer"),
new Customer("David", "Palmer"),
new Customer("Michelle", "Dessler")))
.blockLast(Duration.ofSeconds(10));
}
}
Here is the error that I'm getting
:: Spring Boot :: (v2.3.4.RELEASE)
2020-10-14 15:59:18.538 INFO 25279 --- [ main] i.g.i.repository.CustomerRepositoryTest : Starting CustomerRepositoryTest on imalik8088.fritz.box with PID 25279 (started by imalik in /Users/imalik/code/private/explore-java/spring-example)
2020-10-14 15:59:18.540 INFO 25279 --- [ main] i.g.i.repository.CustomerRepositoryTest : No active profile set, falling back to default profiles: default
2020-10-14 15:59:19.108 INFO 25279 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data R2DBC repositories in DEFAULT mode.
2020-10-14 15:59:19.273 INFO 25279 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 160ms. Found 1 R2DBC repository interfaces.
2020-10-14 15:59:19.894 INFO 25279 --- [ main] o.f.c.internal.license.VersionPrinter : Flyway Community Edition 6.5.0 by Redgate
2020-10-14 15:59:20.052 INFO 25279 --- [ main] o.f.c.internal.database.DatabaseFactory : Database: jdbc:h2:mem:///DBNAME (H2 1.4)
2020-10-14 15:59:20.118 INFO 25279 --- [ main] o.f.core.internal.command.DbValidate : Successfully validated 1 migration (execution time 00:00.022s)
2020-10-14 15:59:20.131 INFO 25279 --- [ main] o.f.c.i.s.JdbcTableSchemaHistory : Creating Schema History table "PUBLIC"."flyway_schema_history" ...
2020-10-14 15:59:20.175 INFO 25279 --- [ main] o.f.core.internal.command.DbMigrate : Current version of schema "PUBLIC": << Empty Schema >>
2020-10-14 15:59:20.178 INFO 25279 --- [ main] o.f.core.internal.command.DbMigrate : Migrating schema "PUBLIC" to version 1.0.0 - schma
2020-10-14 15:59:20.204 INFO 25279 --- [ main] o.f.core.internal.command.DbMigrate : Successfully applied 1 migration to schema "PUBLIC" (execution time 00:00.036s)
2020-10-14 15:59:20.689 INFO 25279 --- [ main] i.g.i.repository.CustomerRepositoryTest : Started CustomerRepositoryTest in 2.466 seconds (JVM running for 3.326)
2020-10-14 15:59:21.115 DEBUG 25279 --- [ main] o.s.d.r2dbc.core.DefaultDatabaseClient : Executing SQL statement [INSERT INTO customer (first_name, last_name) VALUES ($1, $2)]
org.springframework.data.r2dbc.BadSqlGrammarException: executeMany; bad SQL grammar [INSERT INTO customer (first_name, last_name) VALUES ($1, $2)]; nested exception is io.r2dbc.spi.R2dbcBadGrammarException: [42102] [42S02] Tabelle "CUSTOMER" nicht gefunden
Table "CUSTOMER" not found; SQL statement:
INSERT INTO customer (first_name, last_name) VALUES ($1, $2) [42102-200]
My src/test/resources/application.yaml is looking like this:
spring:
r2dbc:
url: r2dbc:h2:mem:///DBNAME?options=DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
username: sa
password:
flyway:
url: jdbc:h2:mem:///DBNAME
baseline-on-migrate: true
user: sa
password:
Any ideas whats missing missing or whats wrong with the setup? If further information is needed please let me know.
Addition/Solution:
The url pattern is different between jdbc and r2dbc. The working solution for me is as follows:
url: r2dbc:h2:file:///./tmp/test-database
url: jdbc:h2:file:./tmp/test-database
And In order to setup Flyway you have to Configure Flyway:
// Flyway is not compatible with r2dbc yet, therefore this config class is created
#Configuration
public class FlywayConfig {
private final Environment env;
public FlywayConfig(final Environment env) {
this.env = env;
}
#Bean(initMethod = "migrate")
public Flyway flyway() {
return new Flyway(Flyway.configure()
.baselineOnMigrate(true)
.dataSource(
env.getRequiredProperty("spring.flyway.url"),
env.getRequiredProperty("spring.flyway.user"),
env.getRequiredProperty("spring.flyway.password"))
);
}
}
I've faced the same issue to setup and access to h2 database in memory for tests:
Liquibase for database migration using JDBC driver
Tests Reactive Crud Repository using R2DBC driver
Error encoutred:
org.springframework.data.r2dbc.BadSqlGrammarException: executeMany; bad SQL grammar [INSERT INTO MY_TABLE... Table "MY_TABLE" not found ...
Inspired by Chris's solution, i configured my src/testresources/application.properties file as follow:
spring.r2dbc.url=r2dbc:h2:mem:///~/db/testdb
spring.r2dbc.username=sa
spring.r2dbc.password=
spring.liquibase.url=jdbc:h2:mem:~/db/testdb;DB_CLOSE_DELAY=-1
spring.liquibase.user=sa
spring.liquibase.password=
spring.liquibase.enabled=true
I am currently having the same problem using r2dbc with liquibase. I am suspecting that the JDBC url points to a different database due to a slightly different syntax between R2DB and JDBC. I can manage to get h2 running from the file system though...
url: r2dbc:h2:file:///~/db/testdb
...
url: jdbc:h2:file:~/db/testdb
EDIT:
In non-reactive Spring Data I'd usually populate the Schema into the H2 memory database using a schema.sql/data.sql pair. This is also possible with R2DBC, but you have to configure the populator yourself.
It's also in the Getting Started R2DBC Tutorial. Basically you have to register a ConnectionFactoryInitializer bean.
#Bean
public ConnectionFactoryInitializer initializer(#Qualifier("connectionFactory") ConnectionFactory connectionFactory) {
var initializer = new ConnectionFactoryInitializer();
initializer.setConnectionFactory(connectionFactory);
var populator = new CompositeDatabasePopulator();
populator.addPopulators(new ResourceDatabasePopulator(new ClassPathResource("schema.sql")));
populator.addPopulators(new ResourceDatabasePopulator(new ClassPathResource("data.sql")));
initializer.setDatabasePopulator(populator);
return initializer;
}
I was able to get it working.
First of all I created following test configuration class (because I want to execute tests only agains H2, on production mode I am using PostgreSQL):
#TestConfiguration
public class TestConfig {
#Bean
#Profile("test")
public ConnectionFactory connectionFactory() {
System.out.println(">>>>>>>>>> Using H2 in mem R2DBC connection factory");
return H2ConnectionFactory.inMemory("testdb");
}
#Bean(initMethod = "migrate")
#Profile("test")
public Flyway flyway() {
System.out.println("####### Using H2 in mem Flyway connection");
return new Flyway(Flyway.configure()
.baselineOnMigrate(true)
.dataSource(
"jdbc:h2:mem:testdb",
"sa",
"")
);
}
}
As you can see in the code above, both beans are scoped to the "test" profile only. As you can imagine I have pretty much the same beans in a regular ApplicationConfiguration class but annotated as a #Profile("default") and configured to use a PostgreSQL.
Second thing is that I created annotation which combines several other annotations to not repeat myself and to easily pickup beans declared in the TestConfig class:
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
#Documented
#Inherited
#SpringBootTest
#ActiveProfiles("test")
#Import(TestConfig.class)
public #interface IntegrationTest {
}
Now the test itself:
#IntegrationTest
class CartsIntegrationTest {
// test methods here ....
}
I believe the main hint is to use H2ConnectionFactory.inMemory("testdb");
Flyway currently only supports the blocking JDBC APIs, and it is not compatible with the reactive r2dbc if possbile do not mix them in the same application.
Try to register a ConnectionFactoryInitializer to initiate the database schema and data as #Chris posted, my working example can be found here.
Try nkonev/r2dbc-migrate which is trying to migrate the flyway to the R2dbc world.
There were 2 issues I was experiencing in my project.
I needed to include the dependency:
<dependency>
<groupId>io.r2dbc</groupId>
<artifactId>r2dbc-h2</artifactId>
<scope>test</scope>
</dependency>
I needed to change the value for spring.r2dbc.url to r2dbc:h2:mem:///test_db
With these changes, rd2bc worked with an in memory h2 database for testing. See also:
https://github.com/r2dbc/r2dbc-h2
Related
I am having an issue with Hibernate.
I am creating tables via Flyway migrations.
A snippet is shown below of part of the initial migration script.
The script does run (I can see it in DEBUG mode).
Following the script running, Hibernate's validator seems to not be using the table name for the entity I have provided it via javax.persistence.
Here is the entity with some omissions for clarity:
import javax.persistence.*;
#Entity
#Table(name = "IngredientCategories")
public class IngredientCategory implements IEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#Column(nullable = false, length = 128)
private String name;
...
}
CREATE TABLE `IngredientCategories` (
`id` bigint NOT NULL AUTO_INCREMENT,
`description` varchar(255) DEFAULT NULL,
`name` varchar(128) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
Looking at the logs, the Flyway migrations are run and then afterwards I get the hibernate validation errors.
2020-10-24 11:22:11.952 DEBUG 91583 --- [ main] o.f.core.internal.command.DbMigrate : Successfully completed migration of schema `test` to version 1 - init
2020-10-24 11:22:11.961 DEBUG 91583 --- [ main] o.f.c.i.s.JdbcTableSchemaHistory : Schema History table `test`.`flyway_schema_history` successfully updated to reflect changes
2020-10-24 11:22:11.970 INFO 91583 --- [ main] o.f.core.internal.command.DbMigrate : Successfully applied 1 migration to schema `test` (execution time 00:00.107s)
2020-10-24 11:22:11.972 DEBUG 91583 --- [ main] org.flywaydb.core.Flyway : Memory usage: 82 of 354M
2020-10-24 11:22:12.088 INFO 91583 --- [ main] o.hibernate.jpa.internal.util.LogHelper : HHH000204: Processing PersistenceUnitInfo [name: default]
2020-10-24 11:22:12.125 INFO 91583 --- [ main] org.hibernate.Version : HHH000412: Hibernate ORM core version 5.4.20.Final
2020-10-24 11:22:12.237 INFO 91583 --- [ main] o.hibernate.annotations.common.Version : HCANN000001: Hibernate Commons Annotations {5.1.0.Final}
2020-10-24 11:22:12.350 INFO 91583 --- [ main] org.hibernate.dialect.Dialect : HHH000400: Using dialect: org.hibernate.dialect.MySQL8Dialect
2020-10-24 11:22:12.998 WARN 91583 --- [ main] s.c.a.AnnotationConfigApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: Invocation of init method failed; nested exception is javax.persistence.PersistenceException: [PersistenceUnit: default] Unable to build Hibernate SessionFactory; nested exception is org.hibernate.tool.schema.spi.SchemaManagementException: Schema-validation: missing table [ingredient_categories]
2020-10-24 11:22:12.998 INFO 91583 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown initiated...
2020-10-24 11:22:13.420 INFO 91583 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown completed.
2020-10-24 11:22:13.428 INFO 91583 --- [ main] ConditionEvaluationReportLoggingListener :
Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2020-10-24 11:22:13.434 ERROR 91583 --- [ main] o.s.boot.SpringApplication : Application run failed
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: Invocation of init method failed; nested exception is javax.persistence.PersistenceException: [PersistenceUnit: default] Unable to build Hibernate SessionFactory; nested exception is org.hibernate.tool.schema.spi.SchemaManagementException: Schema-validation: missing table [ingredient_categories]
I can not decide if this is a hibernate issue, a Flyway issue or a testing issue.
It is just a test "booting" the app:
#SpringBootTest
#Testcontainers
class DataApplicationTests {
#Test
void contextLoads() {
}
}
I have had a look around and can see that many people had an issue before Spring 2x where the tables were validated BEFORE flyway would generate schema...but it seems that that has been "fixed" and the default is Flyway migrations are run before.
The line where I think the issue shows Hibernate was expecting a table name of
Schema-validation: missing table [ingredient_categories]
So it seems to be not using the javax.constaint.Table #Table(name="IngredientCategories") annotation when running the and building the context/beans.
The app properties are not overly exciting...I am using TestContainers:
#=========INTEGRATION TESTS========#
## Using TestContainers
spring.datasource.url=jdbc:tc:mysql:8.0.22:///
# Validate Schema
spring.jpa.hibernate.ddl-auto = validate
logging.level.org.flywaydb=DEBUG
From official doc
By default, Spring Boot configures the physical naming strategy with
SpringPhysicalNamingStrategy. This implementation provides the same
table structure as Hibernate 4: all dots are replaced by underscores
and camel casing is replaced by underscores as well. By default, all
table names are generated in lower case, but it is possible to
override that flag if your schema requires it.
For example, a TelephoneNumber entity is mapped to the
telephone_number table.
So IngredientCategories became ingredient_categories. For database table name general convention to use snake-case. You can create table with name in snake case
CREATE TABLE `ingredient_categories`
Or if you prefer to use Hibernate 5’s default instead , set the following property:
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
Then your table's name remains IngredientCategories as given in #Table annotation. Details about Hibernate 5 Naming Strategy Configuration
A CODE SAMPLE TO HELP FIX THE PROBLEM: https://github.com/Suwappertjes/SpringSample
Problem
When trying to implement jwt-security in a Spring Boot application, I run into the following problem:
When I try to login with x-www-form-urlencoded through Postman I get a "Bad client credentials" error, whilst I know the credentials to be correct.
When I look in my log, I see that BCrypt gave a "Empty Encoded Password" warning. This is odd, considering I see correctly encrypted passwords in the database when I look at it through the MySQL interpreter.
Info
I am using Hibernate to build a MySQL database.
compile 'org.springframework.security.oauth:spring-security-oauth2:2.3.6.RELEASE'
compile 'org.springframework.security:spring-security-jwt:1.0.10.RELEASE'
compile 'org.springframework.boot:spring-boot-starter-data-jpa'
Java 1.8.0_212
What I tried
Before starting to implement security, the controllers, repositories, and MySQL database were all functioning correctly.
When I search for this problem online, some people suggest it has to do with the "loadUserByUsername" function. But when I debug that function I notice it is not being called at all.
I also tried allowing every single path in my program AND disabling crsf
in case it had something to do with access rights, but both didn't change anything. (http.requestMatchers.andMatcher("/**").permitAll().and().csrf().disable();)
Update: When putting NO users in the database, I still get the same error.
Some code:
The loadUserByUsername method:
#Override
public UserDetails loadUserByUsername(String userName) {
return userRepository.findByUsername(userName)
.map(user -> new User(
user.getUsername(),
user.getPassword(),
UserRoleAuthority.getAuthorities(user.getRoles())
))
.orElseThrow(() -> new UsernameNotFoundException("Unknown user: " + userName));
}
The authenticationprovider and passwordencoder:
#Bean
public DaoAuthenticationProvider getAuthenticationProvider() {
DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
authenticationProvider.setUserDetailsService(userDetailsService);
authenticationProvider.setPasswordEncoder(passwordEncoder());
return authenticationProvider;
}
#Bean
PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
The signing key:
#Bean
public JwtAccessTokenConverter jwtTokenEnhancer() {
JwtAccessTokenConverter result = new JwtAccessTokenConverter();
result.setSigningKey(signingKey);
return result;
}
Adding a new user:
userRepository.save(new User()
.withUsername("test")
.withPassword(passwordEncoder.encode("password"))
.withFirst("Admin")
.withLast("Nator")
.withEmail("test#notadmin.com")
.withRole(new HashSet<>(Arrays.asList(Role.Admin, Role.NotAdmin)))
);
And the Http configuration:
#Override
public void configure(HttpSecurity http) throws Exception {
http.requestMatchers()
.antMatchers("/iungo/*/**")
.and()
.authorizeRequests()
.antMatchers("/iungo/system/**")
.permitAll()
.antMatchers("/iungo/**");
}
#Override
public void configure(HttpSecurity http) throws Exception {
// We don't use this endpoint but we have to define it anyway in order to not enable web security on the whole application.
http
.userDetailsService(userDetailsService)
.requestMatchers()
.antMatchers("/oauth/authorize");
}
And finally, my console:
2019-06-12 10:29:58.460 INFO 25207 --- [ main] org.hibernate.Version : HHH000412: Hibernate Core {5.3.9.Final}
2019-06-12 10:29:58.461 INFO 25207 --- [ main] org.hibernate.cfg.Environment : HHH000206: hibernate.properties not found
2019-06-12 10:29:58.532 INFO 25207 --- [ main] o.hibernate.annotations.common.Version : HCANN000001: Hibernate Commons Annotations {5.0.4.Final}
2019-06-12 10:29:58.599 INFO 25207 --- [ main] org.hibernate.dialect.Dialect : HHH000400: Using dialect: org.hibernate.dialect.MySQL5Dialect
2019-06-12 10:29:59.092 INFO 25207 --- [ main] o.h.t.schema.internal.SchemaCreatorImpl : HHH000476: Executing import script 'org.hibernate.tool.schema.internal.exec.ScriptSourceInputNonExistentImpl#c30f26d'
2019-06-12 10:29:59.098 INFO 25207 --- [ main] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default'
2019-06-12 10:29:59.382 WARN 25207 --- [ main] o.s.s.o.p.t.s.JwtAccessTokenConverter : Unable to create an RSA verifier from verifierKey (ignoreable if using MAC)
2019-06-12 10:29:59.595 INFO 25207 --- [ main] o.s.s.web.DefaultSecurityFilterChain : Creating filter chain: OrRequestMatcher [requestMatchers=[Ant [pattern='/oauth/token'], Ant [pattern='/oauth/token_key'], Ant [pattern='/oauth/check_token']]], [org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter#56a72887, org.springframework.security.web.context.SecurityContextPersistenceFilter#1ddba7a0, org.springframework.security.web.header.HeaderWriterFilter#7adbec34, org.springframework.security.web.authentication.logout.LogoutFilter#296bfddb, org.springframework.security.oauth2.provider.client.ClientCredentialsTokenEndpointFilter#22ab1b8a, org.springframework.security.web.authentication.www.BasicAuthenticationFilter#54033a65, org.springframework.security.web.savedrequest.RequestCacheAwareFilter#7dfec0bc, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter#42734b71, org.springframework.security.web.authentication.AnonymousAuthenticationFilter#3c8dea0b, org.springframework.security.web.session.SessionManagementFilter#6fe9c048, org.springframework.security.web.access.ExceptionTranslationFilter#526fc044, org.springframework.security.web.access.intercept.FilterSecurityInterceptor#690e4b00]
2019-06-12 10:29:59.600 INFO 25207 --- [ main] o.s.s.web.DefaultSecurityFilterChain : Creating filter chain: OrRequestMatcher [requestMatchers=[Ant [pattern='/iungo/*/**']]], [org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter#1978b0d5, org.springframework.security.web.context.SecurityContextPersistenceFilter#69a3bf40, org.springframework.security.web.header.HeaderWriterFilter#3186f8f5, org.springframework.security.web.authentication.logout.LogoutFilter#a4dcede, org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationProcessingFilter#760c777d, org.springframework.security.web.savedrequest.RequestCacheAwareFilter#2c731a16, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter#2a341e3d, org.springframework.security.web.authentication.AnonymousAuthenticationFilter#6556471b, org.springframework.security.web.session.SessionManagementFilter#467cd4b9, org.springframework.security.web.access.ExceptionTranslationFilter#3f3f554f, org.springframework.security.web.access.intercept.FilterSecurityInterceptor#d0e4972]
2019-06-12 10:29:59.603 INFO 25207 --- [ main] o.s.s.web.DefaultSecurityFilterChain : Creating filter chain: OrRequestMatcher [requestMatchers=[Ant [pattern='/oauth/authorize']]], [org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter#6cf3b3d7, org.springframework.security.web.context.SecurityContextPersistenceFilter#462f8fe9, org.springframework.security.web.header.HeaderWriterFilter#24f2608b, org.springframework.security.web.csrf.CsrfFilter#713497cd, org.springframework.security.web.authentication.logout.LogoutFilter#56193e3a, org.springframework.security.web.savedrequest.RequestCacheAwareFilter#3c6fc4cd, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter#b2e1df3, org.springframework.security.web.authentication.AnonymousAuthenticationFilter#2e785b28, org.springframework.security.web.session.SessionManagementFilter#12a9e864, org.springframework.security.web.access.ExceptionTranslationFilter#4b762988]
2019-06-12 10:29:59.731 INFO 25207 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor'
2019-06-12 10:29:59.902 INFO 25207 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
2019-06-12 10:29:59.903 INFO 25207 --- [ main] nl.highway.iungomain.Application : Started Application in 2.937 seconds (JVM running for 3.345)
2019-06-12 10:29:59.923 INFO 25207 --- [ main] o.h.h.i.QueryTranslatorFactoryInitiator : HHH000397: Using ASTQueryTranslatorFactory
2019-06-12 10:30:12.550 INFO 25207 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet'
2019-06-12 10:30:12.550 INFO 25207 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'
2019-06-12 10:30:12.554 INFO 25207 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 4 ms
2019-06-12 10:30:12.632 WARN 25207 --- [nio-8080-exec-1] o.s.s.c.bcrypt.BCryptPasswordEncoder : Empty encoded password
I found the problem. I was using Spring boot 2.1.4.RELEASE but this setup only works in 1.5.12.RELEASE. Of course downgrading is not very good practise so I will still try to get it to work with 2.1.4.
you have to add bCryptPasswordEncoder in the configureGlobal method
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(userDetailsService)
.passwordEncoder(bCryptPasswordEncoder());
}
I was facing the exact same problem. In my case, the cause was my CustomUserDetails class constructor, which didn't set the password to the class, so it was always null.
Credits to this anwser:
why spring security gives empty password to password encoder?
I think #ghazouanbadr's solution is correct. Although, I implemented it differently in my practice project:
#EnableWebSecurity
#Configuration
public class WebSecurity extends WebSecurityConfigurerAdapter {
#Autowired
UserService userService;
#Autowired
BCryptPasswordEncoder bCryptPasswordEncoder;
public WebSecurity(UserService userService, BCryptPasswordEncoder bCryptPasswordEncoder) {
this.userService = userService;
this.bCryptPasswordEncoder = bCryptPasswordEncoder;
}
#Override // Authorization
protected void configure(HttpSecurity http) throws Exception {
...
}
#Override // Authentication
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userService(userService).passwordEncoder(bCryptPasswordEncoder);
}
}
Be warned, I was only about a month into learning Java when I created this class. Don't remember all to much about security, so it's not my place to tell you that this is the best way to do it. Since I recall dealing with the same issue I figured it might guide you in the right direction.
I am attempting to turn off auto-commit on Hikari with multiple data sources, but I'm not having any luck. I'm using Spring Boot 2 (2.0.3.RELEASE). Here is my config:
application.properties
spring.datasource.primary.driver=com.mysql.cj.jdbc.Driver
spring.datasource.primary.url=jdbc:mysql://localhost:3306/spark?autoReconnect=true
spring.datasource.primary.username=xxxx
spring.datasource.primary.password=xxxx
spring.datasource.primary.max-active=100
spring.datasource.primary.max-idle=5
spring.datasource.primary.min-idle=1
spring.datasource.primary.test-while-idle=true
spring.datasource.primary.test-on-borrow=true
spring.datasource.primary.validation-query=SELECT 1
spring.datasource.primary.time-between-eviction-runs-millis=5000
spring.datasource.primary.min-evictable-idle-time-millis=60000
spring.datasource.ucm.driver=com.mysql.cj.jdbc.Driver
spring.datasource.ucm.url=jdbc:mysql://localhost:3306/usercentral?autoReconnect=true
spring.datasource.ucm.username=xxx
spring.datasource.ucm.password=xxx
spring.datasource.ucm.max-active=100
spring.datasource.ucm.test-while-idle=true
spring.datasource.ucm.test-on-borrow=true
spring.datasource.ucm.validation-query=SELECT 1
spring.datasource.ucm.time-between-eviction-runs-millis=5000
spring.datasource.ucm.min-evictable-idle-time-millis=60000
spring.datasource.ucm.hikari.auto-commit=false # <- Not working
Here's my configuration class where the data sources are setup
#Primary
#Bean
#ConfigurationProperties("spring.datasource.primary")
public DataSourceProperties primaryDataSourceProperties() {
return new DataSourceProperties();
}
#Bean
#Primary
#ConfigurationProperties("spring.datasource.primary")
public DataSource primaryDataSource() {
return primaryDataSourceProperties().initializeDataSourceBuilder().build();
}
#Bean
#ConfigurationProperties("spring.datasource.ucm")
public DataSourceProperties ucmDataSourceProperties() {
return new DataSourceProperties();
}
#Bean(name="ucmDataSource")
#ConfigurationProperties("spring.datasource.ucm")
public DataSource ucmDataSource() {
return ucmDataSourceProperties().initializeDataSourceBuilder().build();
}
Here is what is being output when the pool is created:
-2018-08-23 15:48:22.845 -DEBUG 21455 --- [nio-8081-exec-1] com.zaxxer.hikari.HikariConfig : 1151 : allowPoolSuspension.............false
-2018-08-23 15:48:22.846 -DEBUG 21455 --- [nio-8081-exec-1] com.zaxxer.hikari.HikariConfig : 1151 : autoCommit......................true
-2018-08-23 15:48:22.846 -DEBUG 21455 --- [nio-8081-exec-1] com.zaxxer.hikari.HikariConfig : 1151 : catalog.........................none
-2018-08-23 15:48:22.846 -DEBUG 21455 --- [nio-8081-exec-1] com.zaxxer.hikari.HikariConfig : 1151 : connectionInitSql...............none
How do I turn off auto-commit on this connection pool?
I know that this is very late but this issue exploded my brain for ~2 days and this post is at the top of google search so I will post here the solution for others.
It's actually quite simple, just that I missed it in the docs here https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#howto-two-datasources
long story short make sure that you add .type(HikariDataSource.class) before you build your class.
Also if you use jhipster you should also point your config to the hikari part like this #ConfigurationProperties("spring.datasource.other.hikari")
I am using Spring Boot 2.0.4.RELEASE, and faced the same problem. Spent hours with related posts showing non-working properties...
Removing .hikari worked for me.
spring.datasource.ucm.autocommit=false
Alternative solution to your problem, configure default-auto-commit property as false. It should also work.
spring.datasource.ucm.default-auto-commit=false
Spring Boot Ref 1
default.auto-commit
Make false default-auto-commit should work.
spring.datasource.ucm.default-auto-commit=false
I am trying to test the access of one of my #RestController which is secured by a custom Spring Security configuration. My use case is the following: A HTTP GET to /someEndpoint is secured with authentification, but a HTTP POST request to the same endpoint is not secured. It's working fine when I boot application and test it with my frontend or Postman.
Now I am trying to write tests with MockMvc with the security configuration. I already made it through a lot of answers on StackOverflow, but nothing helped me.
My test setup looks like the following:
#RunWith(SpringRunner.class)
#WebMvcTest(controllers = MyController.class)
#WebAppConfiguration
#ContextConfiguration
public class AssessmentControllerTest {
private MockMvc mockMvc;
#Autowired
private WebApplicationContext webApplicationContext;
#Before
public void init() throws Exception {
this.mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext)
.alwaysDo(print())
.apply(SecurityMockMvcConfigurers.springSecurity())
.build();
}
// some test methods
}
With this setup all my endpoints are secured and even a HTTP POST is returning a 401 instead of 201. I also enabled the debug log for security and in the debug logs it says that the test uses the default configure(HttpSecurity) and I can't find any of my AntMatchers in the logs:
2018-07-04 19:20:02.829 DEBUG 2237 --- [ main] s.s.c.a.w.c.WebSecurityConfigurerAdapter : Using default configure(HttpSecurity). If subclassed this will potentially override subclass configure(HttpSecurity).
2018-07-04 19:20:03.097 DEBUG 2237 --- [ main] edFilterInvocationSecurityMetadataSource : Adding web access control expression 'authenticated', for org.springframework.security.web.util.matcher.AnyRequestMatcher#1
2018-07-04 19:20:03.127 DEBUG 2237 --- [ main] o.s.s.w.a.i.FilterSecurityInterceptor : Validated configuration attributes
2018-07-04 19:20:03.130 DEBUG 2237 --- [ main] o.s.s.w.a.i.FilterSecurityInterceptor : Validated configuration attributes
2018-07-04 19:20:03.161 INFO 2237 --- [ main] o.s.s.web.DefaultSecurityFilterChain : Creating filter chain: org.springframework.security.web.util.matcher.AnyRequestMatcher#1, [org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter#5a75ec37, org.springframework.security.web.context.SecurityContextPersistenceFilter#3f736a16, org.springframework.security.web.header.HeaderWriterFilter#529c2a9a, org.springframework.security.web.csrf.CsrfFilter#7f93dd4e, org.springframework.security.web.authentication.logout.LogoutFilter#707b1a44, org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter#26c89563, org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter#1e0a864d, org.springframework.security.web.authentication.www.BasicAuthenticationFilter#22ebccb9, org.springframework.security.web.savedrequest.RequestCacheAwareFilter#53abfc07, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter#4aa21f9d, org.springframework.security.web.authentication.AnonymousAuthenticationFilter#2c05ff9d, org.springframework.security.web.session.SessionManagementFilter#26bbe604, org.springframework.security.web.access.ExceptionTranslationFilter#4375b013, org.springframework.security.web.access.intercept.FilterSecurityInterceptor#a96d56c]
2018-07-04 19:20:03.236 INFO 2237 --- [ main] o.s.b.t.m.w.SpringBootMockServletContext : Initializing Spring FrameworkServlet ''
2018-07-04 19:20:03.237 INFO 2237 --- [ main] o.s.t.web.servlet.TestDispatcherServlet : FrameworkServlet '': initialization started
Is it in general possible to use my concrete Spring Security configuration during a MockMvc test or do I have to boot the whole Spring context during the test with #SpringBootTest ? I am using (Spring Boot 2.0.3.RELEASE with Java 1.8)
Thanks in advance!
With the spring-boot 2.x it is not possible to switch of security with a property anymore. You have to write an own SecurityConfiguration which has to be added to your test context. This security config should allow any request without authentication.
#Configuration
#EnableWebSecurity
public class TestSecurityConfiguration extends WebSecurityConfigurerAdapter{
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().authorizeRequests().anyRequest().permitAll();
}
#Override
public void configure(WebSecurity web) throws Exception{
web.debug(true);
}
}
test class annotation:
#ContextConfiguration(classes = { ..., TestSecurityConfiguration.class })
public class MyTests {...
I have a problem and after two days of research, I was not able to find a solution. I have a simple app so far just read all data from table and wanted to write an integration test for it.
Here is my test:
#Transactional
#SpringBootTest
#ActiveProfiles("integrationTest")
class StockFacadeIT extends Specification {
#Autowired
StockFacadeImpl stockFacade
#Autowired
DSLContext dslContext
#Sql(scripts = "/add_sample_stocks.sql")
def 'should return list of ticker in correct order'() {
when:
def tickers = stockFacade.loadAllTickers(dslContext)
println "when cluase"
then:
println "then cluase"
tickers.getAt(0) == 'abc'
tickers.getAt(1) == 'gpw'
tickers.getAt(2) == 'kgh'
tickers.getAt(3) == 'tpe'
}
}
In log I see:
2017-11-06 21:30:09.478 INFO 21124 --- [ main] o.s.t.c.transaction.TransactionContext : Began transaction (1) for test context [DefaultTestContext#6a01e23 testClass = StockFacadeIT, testInstance = com.gpw.radar.stock.StockFacadeIT#455cbf18, testMethod = $spock_feature_0_0#StockFacadeIT, [...] rollback [true]
2017-11-06 21:30:09.478 INFO 21124 --- [ main] o.s.jdbc.datasource.init.ScriptUtils : Executing SQL script from class path resource [add_sample_stocks.sql]
2017-11-06 21:30:09.478 INFO 21124 --- [ main] o.s.jdbc.datasource.init.ScriptUtils : Executed SQL script from class path resource [add_sample_stocks.sql] in 0 ms.
2017-11-06 21:30:09.712 INFO 21124 --- [ main] org.jooq.Constants :
when cluase
then cluase
2017-11-06 21:30:09.869 INFO 21124 --- [ main] o.s.t.c.transaction.TransactionContext : Rolled back transaction for test context [DefaultTestContext#6a01e23 testClass = StockFacadeIT, testInstance = com.gpw.radar.stock.StockFacadeIT#455cbf18, testMethod = $spock_feature_0_0#StockFacadeIT, testException = Condition not satisfied:
So from log perspective I see that transaction begin before test and rolback is run after test ("then clause"). But test doesn't pass beacuase database is empty. When I delete the #Transactional annotation, it passed but inserted records stayed at DB. What am I doing wrong here?
I found where the problem was. As I added spring framework after jooq. My final configuration for jooq looks like:
#Bean
#Profile("integrationTest")
public DSLContext TestDslContext(DataSource dataSource) {
return DSL.using(new DefaultConfiguration()
.set(dataSource)
.set(new Settings().withRenderNameStyle(RenderNameStyle.AS_IS))
.set(SQLDialect.H2)
.set(getRecordMapperProvider())
);
}
So when I remove this part and use auto config of spring boot the transactions works, however, I have checked how DSLContext config looks when is created by spring I have TransactionProvider and ExecuteListenerProvider missing. When I inject it and set to my custom config the transactions still does not work.