I am learning spring security with JPA auth againest H2 table. It fails load seed data from data.sql
I tried access the H2 console, but doesn't let me login. Any suggestions appreciated
applicaiton.properties
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=password
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
spring.h2.console.enabled=true
User.java
#Entity
#Table(name = "USER_TABLE")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
#Column(name="USER_NAME")
private String userName;
- - - - - -
- - - - - -
}
data.sql
insert into USER_TABLE (id, USER_NAME, PASSWORD, ACTIVE, ROLES) values
(1, 'einstein', 'einstein', true, 'USER'),
(2, 'newton', 'newton', true, 'USER'),
(3, 'koushik', 'koushik', true, 'USER');
SecurityConfig.java
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
MyUserDetailsService userDetailsService;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
}
............
............
}
MyUserService.java
#Service
public class MyUserDetailsService implements UserDetailsService {
#Autowired
UserRepository userRepository;
#Override
public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
Optional<User> user = userRepository.findByUserName(userName);
user.orElseThrow(() -> new UsernameNotFoundException("Not found: " + userName));
return user.map(MyUserDetails::new).get();
}
}
I get below error, while starting the spring-boot
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'dataSourceScriptDatabaseInitializer' defined in class path resource [org/springframework/boot/autoconfigure/sql/init/DataSourceInitializationConfiguration.class]: Invocation of init method failed; nested exception is org.springframework.jdbc.datasource.init.ScriptStatementFailedException: Failed to execute SQL script statement #1 of URL [file:/C:/resume-portal/target/classes/data.sql]: insert into USER_TABLE (id, USER_NAME, PASSWORD, ACTIVE, ROLES) values (1, 'einstein', 'einstein', true, 'USER'), (2, 'newton', 'newton', true, 'USER'), (3, 'koushik', 'koushik', true, 'USER'); nested exception is org.h2.jdbc.JdbcSQLSyntaxErrorException: Table "USER_TABLE" not found (this database is empty); SQL statement:
insert into USER_TABLE (id, USER_NAME, PASSWORD, ACTIVE, ROLES) values (1, 'einstein', 'einstein', true, 'USER'), (2, 'newton', 'newton', true, 'USER'), (3, 'koushik', 'koushik', true, 'USER') [42104-214]
Many thanks to the community.
Since your database generates a unique id for you, you do not have to provide any id in the insert statement.
insert into USER_TABLE (USER_NAME, PASSWORD, ACTIVE, ROLES) values
('einstein', 'einstein', true, 'USER'),
('newton', 'newton', true, 'USER'),
('koushik', 'koushik', true, 'USER');
Change your DML to define no id to solve your problem.
Alternatively, you can create instances of your entity with id = null in a command line runner and add them to the repository.
#Component
public class Initializer implements CommandLineRunner {
#Autowired
UserRepository repository;
#Override
public void run(String... args) throws Exception {
//Pseudocode
User user = new User(id=null,name="Albert Einstein" ...)
repository.save(user)
}
}
Related
I recently asked a question very similar to this one but instead of 401 the error I was getting was 403 (Forbbiden), but I changed the entire code so I decided to post a new one specific to this code and this problem.
I'm trying to create an user logic to my project (for the first time ever) but it has been impossible to implement any kind of security measure. I've been stuck in this for days so if anyone knows where I'm wrong I'd be grateful!
this is my code:
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter{
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/users/create", "/users/create/**").permitAll()
.and()
.httpBasic();
}
}
#Data
#Component
public class CreateUserRoleDTO {
private Integer idUser;
private List<Integer> idsRoles;
public CreateUserRoleDTO() {
super();
}
public CreateUserRoleDTO(Integer idUser, List<Integer> idsRoles) {
super();
this.idUser = idUser;
this.idsRoles = idsRoles;
}
public Integer getIdUser() {
return idUser;
}
public void setIdUser(Integer idUser) {
this.idUser = idUser;
}
public List<Integer> getIdsRoles() {
return idsRoles;
}
public void setIdsRoles(List<Integer> idsRoles) {
this.idsRoles = idsRoles;
}
}
#Service
public class CreateRoleUserService {
#Autowired
private UserRepository repo;
#Autowired
private CreateUserRoleDTO createUserRoleDTO;
public Users execute(CreateUserRoleDTO createUserRoleDTO) {
Optional<Users> userExists=repo.findById(createUserRoleDTO.getIdUser());
List<Roles> roles=new ArrayList<>();
if (userExists.isEmpty()) {
throw new Error("User does not exist");
}
roles=createUserRoleDTO.getIdsRoles().stream().map(role -> {
return new Roles(role);
}).collect(Collectors.toList());
Users user=userExists.get();
user.setRole(roles);
repo.save(user);
return user;
}
#Entity
#Table(name="users_table")
public class Users implements Serializable{
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Integer id;
#Column(unique=true)
private String login;
#Column(unique=true)
private String email;
private String password;
#ManyToMany
private List<Roles> role;
}
(plus the getters and setters and constructors)
data.sql:
INSERT INTO `ROLES`(`ID`, `NAME`) VALUES(1, 'USER');
INSERT INTO `ROLES`(`ID`,`NAME`) VALUES(2, 'ADMIN');
-> the code runs fine, it even gives me the security password, the problem appears when I try to make any kind of requests.
The entire code if I've left anything out: https://github.com/vitoriaacarvalho/backend-challenge-very-useful-tools-to-remember-
An authentication configuration is missing in your SecurityConfig. For example, try adding the following to your configure method:
http.httpBasic();
Additionally, your security configuration is missing a default authorization rule, so authentication is not actually required. You can try adding .anyRequest().authenticated() to test this out.
Here's a configuration which uses the lambda syntax available in the DSL and is ready to be upgraded to Spring Security 6:
#Configuration
#EnableWebSecurity
#EnableMethodSecurity
public class SecurityConfig {
#Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests((authorize) -> authorize
.antMatchers("/users/create", "/users/create/**").permitAll()
.anyRequest().authenticated()
)
.httpBasic(Customizer.withDefaults());
// Disable CSRF for testing.
// TODO: Delete the following line and learn about CSRF!
http.csrf().disable();
return http.build();
}
#Bean // Automatically injected into Spring Security
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
// Note: We don't configure a UserDetailsService since it is already
// annotated #Service and therefore already published as an #Bean.
}
Unfortunately, I also spotted a few other mistakes in your application that made it not work.
It looks like you have a mistake in the JPQL used to query the user for the UserDetailsService. The WHERE clause should be where u.login = :username (add a u.).
You also have the if-statement inverted as well. When throwing a UsernameNotFoundException (a better exception than Error for this case), it would look like:
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Users user = repo.findByUsernameFetchRoles(username);
if (user == null) {
throw new UsernameNotFoundException("User does not exist!");
}
return UserPrincipal.create(user);
}
Lastly, the constructor of your Users class was not assigning user data from the user parameter. It should be:
public UserPrincipal(Users user) {
this.login = user.getLogin();
this.password = user.getPassword();
...
}
With those changes, authentication works and you're on your way to learning Spring Security!
A bean definition of type org.springframework.jdbc.core.JdbcTemplate was not found, therefore #Autowired private JdbcTemplate jdbcTemplate; doesn't actually have a value inside it.
My Application.java looks as following:
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Value("${spring.name}")
private String name;
#Autowired
private JdbcTemplate jdbcTemplate;
private static final Logger log = LoggerFactory.getLogger(Application.class);
// #Bean
// public CommandLineRunner commandLineRunner(ApplicationContext ctx) {
// return args -> {
// System.out.printf("The application is running %s!", name);
// };
// }
public void run(String... strings) throws Exception {
log.info("Creating tables");
jdbcTemplate.execute("DROP TABLE customers IF EXISTS");
jdbcTemplate.execute("CREATE TABLE customers(" +
"id SERIAL, first_name VARCHAR(255), last_name VARCHAR(255))");
// Split up the array of whole names into an array of first/last names
List<Object[]> splitUpNames = Arrays.asList("John Woo", "Jeff Dean", "Josh Bloch", "Josh Long").stream()
.map(name -> name.split(" "))
.collect(Collectors.toList());
// Use a Java 8 stream to print out each tuple of the list
splitUpNames.forEach(name -> log.info(String.format("Inserting customer record for %s %s", name[0], name[1])));
// Uses JdbcTemplate's batchUpdate operation to bulk load data
jdbcTemplate.batchUpdate("INSERT INTO customers(first_name, last_name) VALUES (?,?)", splitUpNames);
log.info("Querying for customer records where first_name = 'Josh':");
jdbcTemplate.query(
"SELECT id, first_name, last_name FROM customers WHERE first_name = ?", new Object[] { "Josh" },
(rs, rowNum) -> new CustomerModel(rs.getLong("id"), rs.getString("first_name"), rs.getString("last_name"))
).forEach(customer -> log.info(customer.toString()));
}
I understand Dependency Injection and IoC which should technically instantiate a JdbcTemplate instance by itself, but if I do it manually it I have the following code which gives the error that JdbcTemplate bean requires dataSource property (which I'm giving as below):
#Value("${spring.datasource.url}")
private String dbUrl;
#Value("${spring.datasource.username}")
private String dbUsername;
#Value("${spring.datasource.password}")
private String dbPassword;
private DataSource dataSource = new DriverManagerDataSource(dbUrl, dbUsername, dbPassword);
private JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
These lines are not producing Spring beans, so they are not candidates for autowiring:
private DataSource dataSource = new DriverManagerDataSource(dbUrl, dbUsername, dbPassword);
private JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
If you're using Spring Boot, you can follow these instructions to configure your datasource, though make sure you use the spring-boot-starter-jdbc dependency in your pom.
If you're configuring these manually, you need to create a #Configuration class, that exposes both a DataSource and JdbcTemplate bean. For example, something like:
#Configuration
public class DatabaseConfiguration {
#Value("${spring.datasource.url}")
private String dbUrl;
#Value("${spring.datasource.username}")
private String dbUsername;
#Value("${spring.datasource.password}")
private String dbPassword;
#Bean
public DataSource dataSource() {
return new DriverManagerDataSource(dbUrl, dbUsername, dbPassword);
}
#Bean
public JdbcTemplate jdbcTemplate() {
return new JdbcTemplate(dataSource);
}
}
I have a Spring boot neo4j application and want to store users in the Neo4j database. I followed the instructions found here.
The neo4j configuration is this:
#Configuration
#EnableWebMvc
#ComponentScan({"eu.bm"})
#EnableNeo4jRepositories("eu.bm.repository")
#EnableTransactionManagement
public class bmConfiguration extends WebMvcConfigurerAdapter {
#Bean
public OpenSessionInViewInterceptor openSessionInViewInterceptor() {
OpenSessionInViewInterceptor openSessionInViewInterceptor =
new OpenSessionInViewInterceptor();
openSessionInViewInterceptor.setSessionFactory(sessionFactory());
return openSessionInViewInterceptor;
}
public void addInterceptors(InterceptorRegistry registry) {
registry.addWebRequestInterceptor(openSessionInViewInterceptor());
}
#Bean
public static SessionFactory sessionFactory() {
return new SessionFactory("eu.bm.domain");
}
#Bean
public Neo4jTransactionManager transactionManager() throws Exception {
return new Neo4jTransactionManager(sessionFactory());
}
The user repository is this:
#Repository
public interface UserRepository extends GraphRepository<User>{
#Query("MATCH (user:User) where user.name={0} return user ")
User findUser(String username);
#Query("MATCH (user:User) where user.name={0} delete user ")
User deleteUser(String username);
#Query("match (user:User) delete user")
User deleteAllUsers();
}
I also have this User management service setup:
#Component
public interface UserManagementService {
List<User> listAll();
User save(User user);
User findUser(String username);
}
which is implemented here:
#Service
#Transactional
public class UserManagementServiceImpl implements UserManagementService {
private UserRepository userRepository;
#Autowired
public UserManagementServiceImpl(UserRepository userRepository) {this.userRepository = userRepository;}
#Override
public List<User> listAll() {
List<User> users = new ArrayList<>();
userRepository.findAll().forEach(users::add);
return users;
}
#Override
public User save(User user) {
userRepository.save(user);
return user;
}
#Override
public User findUser(String username) {
return userRepository.findUser(username);
}
I then perform a simple write-read test like this:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest
public class DatabaseConnectionTest {
// User 1
private static final String UNAME1 = "Paul";
private static final String EMAIL1 = "paul#user.com";
private static final String PASSWORD1 = "p#ss";
private static final String USERNAME1 = "paul";
#Autowired
private UserRepository userRepository;
#Before
public void setUp() throws Exception {
// Clear database before adding new user
userRepository.deleteAllUsers();
// Creating user
User user = new User(UNAME1, USERNAME1, PASSWORD1, EMAIL1);
userRepository.save(user);
}
#Test
public void testPersistence() {
Assert.assertEquals(UNAME1, userRepository.findUser(UNAME1).getName());
Assert.assertEquals(USERNAME1, userRepository.findUser(UNAME1).getUsername());
}
The result of the above is this error:
2017-08-16 14:59:38.990 INFO 6496 --- [ main] e.b.repository.DatabaseConnectionTest : Started DatabaseConnectionTest in 7.131 seconds (JVM running for 8.331)
2017-08-16 14:59:39.872 INFO 6496 --- [ main] o.n.o.drivers.http.request.HttpRequest : Thread: 1, url: http://localhost:7474/db/data/transaction/39, request: {"statements":[{"statement":"UNWIND {rows} as row CREATE (n:User) SET n=row.props RETURN row.nodeRef as ref, ID(n) as id, row.type as type","parameters":{"rows":[{"nodeRef":-1821370276,"type":"node","props":{"password":"p#ss","name":"Paul","email":"paul#user.com","username":"paul"}}]},"resultDataContents":["row"],"includeStats":false}]}
2017-08-16 14:59:40.358 ERROR 6496 --- [ main] o.s.d.n.t.Neo4jTransactionManager : Commit exception overridden by rollback exception
org.springframework.dao.InvalidDataAccessApiUsageException: Transaction is not current for this thread; nested exception is org.neo4j.ogm.exception.TransactionManagerException: Transaction is not current for this thread
Why does userRepository.save(user) creates the record in the database but throws the exception?
What does "Transaction is not current for this thread" excactly mean?
Why does userRepository.save(user) ultimately failes but userRepository.deleteAllUsers() works?
edit: My dependencies.gradle includes the following:
compile group: 'org.springframework.boot', name: 'spring-boot-starter-data-neo4j', version: '1.5.6.RELEASE'
//compile group: 'org.springframework.data', name: 'spring-data-neo4j', version: '4.2.6.RELEASE'
compile group: 'org.neo4j', name: 'neo4j-ogm-core', version: '2.1.3'
//compile group: 'org.neo4j', name: 'neo4j-ogm-bolt-driver', version: '2.1.3'
runtime group: 'org.neo4j', name: 'neo4j-ogm-http-driver', version: '2.1.3'
compile('org.springframework.boot:spring-boot-starter-security')
compile('org.springframework.boot:spring-boot-starter-thymeleaf')
compile('org.springframework.boot:spring-boot-starter-web')
compile group: 'org.springframework.boot', name: 'spring-boot-starter-actuator', version: '1.5.4.RELEASE'
testCompile('org.springframework.boot:spring-boot-starter-test')
Your SessionFactory bean should not be declared as static:
#Bean
public static SessionFactory sessionFactory() {
return new SessionFactory("eu.bm.domain");
}
should be
#Bean
public SessionFactory sessionFactory() {
return new SessionFactory("eu.bm.domain");
}
Background
"Transaction is not current for this thread" means that something is trying to commit a transaction which is different than current tx in a thread local context.
You are using default transaction management - it creates a transaction around repository calls.
When the SessionFactory bean is defined as static the Session somehow doesn't see the transaction in thread local (the one started by the default tx management) and creates new one, tries to commit it and the exception appears.
I am using Spring boot and trying to set transactions to work properly.
This is my main class:
#Configuration
#EnableJpaRepositories
#EnableAutoConfiguration
#EnableTransactionManagement
#ComponentScan(basePackages = "com.aa.bb")
public class WebApplication {
public static void main(String[] args) throws Exception {
SpringApplication.run(WebApplication.class, args);
}
}
This is my service:
#Service
#Path("/users")
public class UserRestServices {
#Autowired
private UserDao userDao;
#Autowired
private LogRecordDao logRecordDao;
#POST
#Transactional(rollbackFor = Exception.class)
public String saveUser(User user) {
userDao.create(user);
logUser(user);
return "SUCCESS";
}
private void logUser(User user) {
LogRecord log = new LogRecord();
log.setClassName(this.getClass().getSimpleName());
log.setText("User was created " + user.getName());
logRecordDao.create(log);
}
}
The create user works well as it should.
The logUser in the logRecordDao is implemented for exception:
public void create(LogRecord entity) {
Query query = sessionFactory.getCurrentSession().createSQLQuery("UPDATE AAA SET lll = 3");
query.executeUpdate();
}
There is no such table therefore I expect a rollback.
There isn't any.
The user is saved even though an exception occured:
Log:
Hibernate:
insert
into
users
(name, userName)
values
(?, ?)
Hibernate:
UPDATE
AAA
SET
lll = 3
2015-09-12 13:35:28.645 WARN 34974 --- [nio-8080-exec-1] o.h.engine.jdbc.spi.SqlExceptionHelper : SQL Error: 1146, SQLState: 42S02
2015-09-12 13:35:28.646 ERROR 34974 --- [nio-8080-exec-1] o.h.engine.jdbc.spi.SqlExceptionHelper : Table 'db.AAA' doesn't exist
I tried to set #EnableTransactionManagement(mode = AdviceMode.PROXY)
didn't work.
Any ideas?
Thanks,
id
So, how can I get the user's id from a current logged user from any social providers?
Well I know I can build a custom SocialUser, the same I do for User, but in the case there is no getter on SocialUserDetails and the method I got just accepts userDetails, instead of a normal "Person" entity.
public class SocialUsersDetailServiceImpl implements SocialUserDetailsService {
private UserDetailsService service;
public SocialUsersDetailServiceImpl(UserDetailsService service) {
this.service = service;
}
#Override
public CSocialUserDetails loadUserByUserId(String username) throws UsernameNotFoundException, DataAccessException {
UserDetails userDetails = (UserDetails) service.loadUserByUsername(username);
return new CustomSocialUser(userDetails.getUsername(), userDetails.getPassword(), userDetails.getAuthorities(), userDetails.getUserIdentifier()??);
}
}
But there is no ".getUserIdentifier()" method on UserDetails, there is some workaround for this?
The way I do for User:
#Service
public class UserDetailsServiceImpl implements CUserDetailsService {
#Resource
private PersonRepository respository;
#Override
public CUserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Person p = repository.findByUsername(username);
return new CUser(p.getUsername(), p.getPassword(), p.grantedAuthorities(), p.getIdPerson());
}
}
and the CUser:
public class CUser extends User{
private Number identifier;
public CUser(String username, String password, Collection<? extends GrantedAuthority> authorities, Number identifier) {
super(username, password, authorities);
this.identifier = identifier;
}
public Number getUserIdentifier() {
return identifier;
}
}