How to fix bad credentials error - spring security

208 Views Asked by At

As soon as I fire up server i'm being redirected to login page, which always gives bad credentials.

I have tried all that i could understand

package com.example.anime_website_backend.security.config;


import com.example.anime_website_backend.user.AppUserService;
import lombok.AllArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
@AllArgsConstructor
public class WebSecurityConfig {

    private final AppUserService appUserService;
    private final BCryptPasswordEncoder bCryptPasswordEncoder;

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
                .authorizeHttpRequests((authorizeRequests) ->
                        authorizeRequests
                                .requestMatchers("api/v1/register").permitAll()
                                .requestMatchers("/admin/**").hasRole("ADMIN")
                                .requestMatchers("/**").hasRole("USER"))
                .httpBasic(Customizer.withDefaults())
                .formLogin(Customizer.withDefaults());

        return http.build();
    }



    @Bean
    public DaoAuthenticationProvider daoAuthenticationProvider(){
        DaoAuthenticationProvider provider=new DaoAuthenticationProvider();
        provider.setPasswordEncoder(bCryptPasswordEncoder);
        provider.setUserDetailsService(appUserService);

        return provider;
    }
}

This is my websecurityconfig

# JPA configuration
spring.jpa.hibernate.ddl-auto=create-drop
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect
spring.jpa.properties.hibernate.format_sql=true
spring.jpa.show-sql=true

spring.security.user.name=user
spring.security.user.password=password


server.port=4000

This is the porperties file.

enter image description here

I hope anyone can tell me where I have gone wrong. I tried searching all places, but spring 6 has deprecated many things and im at a loss. Tried chatgpt etc., but no help.

package com.example.anime_website_backend.user;

import lombok.AllArgsConstructor;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;


@Service
@AllArgsConstructor
public class AppUserService implements UserDetailsService {


    private final AppUserRepository appUserRepository;

    @Override
    public UserDetails loadUserByUsername(String email)
            throws UsernameNotFoundException{
        AppUser appUser= appUserRepository
                .findByEmail(email)
                .orElseThrow(()->
                        new UsernameNotFoundException(
                        String
                                .format(AppUserRelatedMessages
                                        .USER_NOT_FOUND
                                        .getMessage(email))
                ));

        List<GrantedAuthority> authorities=new ArrayList<>();
        for(String role:appUser.getRoles()){
            authorities.add(new SimpleGrantedAuthority(role));
    }
        return new org
                .springframework
                .security
                .core
                .userdetails
                .User(
                    appUser.getEmail(),
                    appUser.getPassword(),
                    authorities
        );

    }

}

this is the AppUser class

i get a feeling that nothing from application.properties is being read ad when i comment the spring.security.user.password=password the auto generated password isnt appearing in console.

1

There are 1 best solutions below

7
On

You just need determine in your database Entity, that implements user details service like this: (or you can just map your entity on UserDetails later)


@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@Builder

@Entity
@Table(name = "t_accounts")
public class Account implements UserDetails {
    @Id
    @Column(nullable = false, name = "account_id")
    private Long id;

    private String email;

    private String password;

    @Enumerated(EnumType.STRING)
    @Column(length = 20)
    private Role role;

    private boolean isAccountNonLocked;

    private boolean isEnabled;

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return Set.of(role);
    }

    @Override
    public String getUsername() {
        return email;
    }

    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }
}

Role enum:


public enum Role implements GrantedAuthority {
    USER, ADMIN;

    @Override
    public String getAuthority() {
        return name();
    }
}

Account repository:


public interface AccountRepository extends JpaRepository<Account, Long> {
    Optional<Account> findByEmail(String email);
}

You must have next Beans: UserDetailsService (you already have this one, but I recommend separate UserService and UserDetailsService) and AuthenticationProvider.


    @Bean
    public UserDetailsService userDetailsService(AccountRepository accountRepository) {
        return email -> accountRepository.findByEmail(email)
                .orElseThrow(() -> new ResponseStatusException(
                        HttpStatus.NOT_FOUND,
                        "user with email = %s not found".formattet(email)
                ));
    }

    @Bean
    public AuthenticationProvider authenticationProvider() {
        DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
        authenticationProvider.setUserDetailsService(userDetailsService);
        authenticationProvider.setPasswordEncoder(passwordEncoder);
        return authenticationProvider;
    }

And your configuration will be like:


@Configuration
@RequiredArgsConstructor //Generates a constructor with required arguments.
public class WebSecurityConfig {

    private final UserDetailsService userDetailsService; //here you can use any bean, that implements UserDetailsService interface
    private final PasswordEncoder passwordEncoder; //here you can use bean, that implements PasswordEncoder interface

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
                .authorizeHttpRequests((authorizeRequests) ->
                        authorizeRequests
                                .requestMatchers("api/v1/register").permitAll()
                                .requestMatchers("/admin/**").hasRole("ADMIN")
                                .requestMatchers("/**").hasRole("USER"))
                .httpBasic(Customizer.withDefaults())
                .formLogin(Customizer.withDefaults());

        return http.build();
    }



    @Bean
    public DaoAuthenticationProvider daoAuthenticationProvider(){
        DaoAuthenticationProvider provider=new DaoAuthenticationProvider();
        provider.setPasswordEncoder(bCryptPasswordEncoder);
        provider.setUserDetailsService(userDetailsService);

        return provider;
    }
}

And of course you must have in your database already defined users:


INSERT INTO t_accounts(account_id, email, password, role, is_account_non_locked, is_enabled)
VALUES
    (1, '[email protected]', '$2a$10$5d3w9sGDT47bFKKFw9oK8OvbOPH2lJCH2p3caVVwZtUIval2OFys2', 'USER', true, true),
    (2, '[email protected]', '$2a$10$rVjZv3bOHS7IkhKMsjn9X.boQ7GdGW14S7K4QtoKXKxynKlIlfnRa', 'USER', true, true);

Passwords are hashed, so in database they store like that, I just hashed 'password' value with my BCryptPasswordEncoder. If you don't want to encrypt your passwords (it's a bad idea) - you can read about NoOpPasswordEncoder - that is deprecated.