0

I am building a Spring application which uses a database as the Authorisation Service in an OAuth2 configuration.

Here is my SecurityConfig class for Spring Security

public class SecurityConfiguration extends WebSecurityConfigurerAdapter {


@Autowired
private CustomUserDetailsService userDetailsService;

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {

    auth.userDetailsService(userDetailsService)
            .passwordEncoder(getPasswordEncoder());
}


@Override
protected void configure(HttpSecurity http) throws Exception {

    http.csrf().disable();
    http.authorizeRequests()
            .antMatchers("**/secured/**").authenticated()
            .anyRequest().permitAll()
            .and()
            .formLogin().permitAll();
}

private PasswordEncoder getPasswordEncoder() {
    return new PasswordEncoder() {
        @Override
        public String encode(CharSequence charSequence) {
            return charSequence.toString();
        }

        @Override
        public boolean matches(CharSequence charSequence, String s) {
            return true;
        }
    };
}
}

Here is my Repository for my database, which aims to locate users on their email.

@Repository
public interface UsersRepository extends JpaRepository<User, Integer> {
   @Query
   Optional<User> findByEmail(String email);
}

Here is my Service class:

@Service("userDetailsService")
public class CustomUserDetailsService implements UserDetailsService {

@Autowired
private UsersRepository usersRepository;


@Override
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
    Optional<User> optionalUsers = usersRepository.findByEmail(email);

    optionalUsers
            .orElseThrow(() -> new UsernameNotFoundException("Username not found"));
    return optionalUsers
            .map(CustomUserDetails::new).get();


}
}

When I type an email and password I have on the database it comes back saying

"Bad Credentials"

Can anybody spot anything wrong with my set up?

If I remove the

@PreAuthorise("hasRole('ROLE_admin')")

in the controller which would get rid of the login screen but I wish to have the login screen.

As requested by the comments, here is my database schema. I use H2 to provide an in memory database.

DROP TABLE IF EXISTS 'User'

CREATE TABLE IF NOT EXISTS User (
id INT,
role VARCHAR(5),
title VARCHAR(5),
firstname VARCHAR(20),
lastname VARCHAR(20),
email VARCHAR(50),
password VARCHAR(50),
modified DATETIME,
accessed DATETIME
)

INSERT INTO User VALUES
(
 '1',
 'admin',
 'mr',
 'bob',
 'smith',
 'bob.smith@example.com',
 'gobob',
 '1993-10-25 22:10:00',
 '2018-04-09 08:30:00'
 ),
 ....

spring.h2.console.enabled=true
spring.h2.console.path=/h2
# Datasource
spring.datasource.url=jdbc:h2:file:path/to/application/src/main/resources/DATA-DUMP.sql
spring.datasource.username=<user>
spring.datasource.password=<pass>
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.testWhileIdle=true
spring.datasource.validationQuery=SELECT 1
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=update
spring.jpa.hibernate.naming-     strategy=org.hibernate.cfg.ImprovedNamingStrategy
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5Dialect

I don't think my H2 is populating my Table it creates:

2018-04-17 13:52:43.523  INFO 4407 --- [           main] o.hibernate.annotations.common.Version   : HCANN000001: Hibernate Commons Annotations {5.0.1.Final}
2018-04-17 13:52:43.583  INFO 4407 --- [           main] org.hibernate.dialect.Dialect            : HHH000400: Using dialect: org.hibernate.dialect.MySQL5Dialect
Hibernate: create table hibernate_sequence (next_val bigint) engine=MyISAM
Hibernate: insert into hibernate_sequence values ( 1 )
Hibernate: create table user (id integer not null, accessed datetime, email varchar(255), firstname varchar(255), lastname varchar(255), modified datetime, password varchar(255), role varchar(255), title varchar(255), primary key (id)) engine=MyISAM
2018-04-17 13:52:43.881  INFO 4407 --- [           main] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default'
shirafuno
  • 387
  • 3
  • 9
  • 24
  • Does loadUserByUsername ever get called in your configuration? (can you try to add a `System.out.println(...)` in `loadUserByUsername ` and tell us if it gets called?) From what I can see, even if you have the wrong password, it'll let you log in because your password encoder implementation just returns true, so it clearly means that the username can't even be found, not that the password is wrong. – TwiN Apr 17 '18 at 11:46
  • Yes the method gets called. I've just realised it leaves a String within the console:------------------------------------------------------------------------------------------------------------------------------------------------------- Hibernate: select user0_.id as id1_0_, user0_.accessed as accessed2_0_, user0_.email as email3_0_, user0_.firstname as firstnam4_0_, user0_.lastname as lastname5_0_, user0_.modified as modified6_0_, user0_.password as password7_0_, user0_.role as role8_0_, user0_.title as title9_0_ from user user0_ where user0_.email=? – shirafuno Apr 17 '18 at 11:50
  • Alright, at the top of your `loadUserByUsername`, can you check how much entries your user repository has? ex. `System.out.println(usersRepository.count());` – TwiN Apr 17 '18 at 11:52
  • 0... :<. Does this mean my database isn't being properly read? – shirafuno Apr 17 '18 at 12:00
  • Yes, it seems like it. If you're populating your database programmatically, can you provide us with a snippet of the code? – TwiN Apr 17 '18 at 12:04
  • You said that you `use H2 to provide an in memory database.`, but I don't see any configuration related to h2. If you're just testing with it, you might be better off programmatically populating your h2 database (with, for instance, a CommandLineRunner) since you'll be using a MySQL database that already has users in it in production anyways. – TwiN Apr 17 '18 at 12:32
  • I realised this afterwards that my database isn't directly set by H2. it somehow read the .sql file in my resources without being told to. I have no changed my application.properties. – shirafuno Apr 17 '18 at 12:33

1 Answers1

1

Although you're currently getting a Bad Credentials message, you might run into another problem afterward:

@PreAuthorise("hasRole('ROLE_admin')")

Note that hasRole is case sensitive, and it might be possible that the correct role is ROLE_ADMIN. Also, depending on your version of Spring Security, you might need to omit ROLE_ and simply use

@PreAuthorise("hasRole('ADMIN')")

The problem

Like I mentioned in the comments, with your implementation of PasswordEncoder, the password you use to login doesn't matter as long as the username exists because your implementation's matches method always return true.

In other words, the problem is likely to be that your repository cannot find any user with the username you're trying to test.

Your database is most likely empty.

UPDATE

The User table is automatically created by Hibernate after reading all class annotated with @Entity, not because you wrote it in your schema file.

Create a file named data-h2.sql in src/main/resources and move all insertions there, e.g.:

INSERT INTO User VALUES (
  '1',
  'admin',
  'mr',
  'bob',
  'smith',
  'bob.smith@example.com',
  'gobob',
  '1993-10-25 22:10:00',
  '2018-04-09 08:30:00'
),
...

See Spring Boot - Loading Initial Data for further details

TwiN
  • 3,554
  • 1
  • 20
  • 31