Home > Net >  Spring Security registration bug
Spring Security registration bug

Time:01-12

I faced a terrible bug in my login/registration program.

The main idea is to register, press submit and all the data had to be stored in postgres database, and after that user should login(all project code provided below) to his profile. But when I try to register and submit all data, the password field become empty, however other fields is normally filled, and of course when I tried to login with email and password I'm getting error message(Invalid email or password which I created for such situations).

I think the issue maybe with password encoding, but I don't have idea how to fix this.

Security config

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig {

        @Bean
        public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
            http
            .csrf()
            .disable()
            .authorizeRequests()
            .antMatchers("/").permitAll() 
            .antMatchers("/css/**").permitAll()
            .antMatchers("/js/**").permitAll()
            .antMatchers("/img/**").permitAll()
            .antMatchers("/registration/**").permitAll()
            .antMatchers("/").permitAll()
            .anyRequest()
            .authenticated()
            .and()
            .formLogin()
            .loginPage("/login").permitAll()
            .loginProcessingUrl("/login")
            .defaultSuccessUrl("/profile")
            .and()
            .logout()
            .logoutUrl("/logout")
            .logoutSuccessUrl("/")
            .and()
            .httpBasic();

            return http.build();
    }
        
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

}

Authorization controller

    @AllArgsConstructor
@Controller
public class AuthController {
 
    private final EmployeeService employeeService;

    @GetMapping("/") 
    public String getHomePage() {
        return "home";
    }
 
    @GetMapping("/login")
    public String getLoginPage() {
        return "login";
    }

    @GetMapping("/profile")
    public String getSuccessPage() {
        return "profile";
    }
 
    @GetMapping("registration")
    public String getRegisterPage(Model model) {
        EmployeeDto employee = new EmployeeDto();
        model.addAttribute("user", employee);
        return "registration";
    }

    @PostMapping("/registration/save")
    public String postRegisterPage(@Valid @ModelAttribute("user") EmployeeDto employee,
            BindingResult result,
            Model model){
        Optional<Employee> existing = employeeService.findByEmail(employee.getEmail());
        if (existing != null) {
            result.rejectValue("email", null, "There is already an account registered with that email");
        }
         
     
        
        if (result.hasErrors()) {
            model.addAttribute("user", employee);
            return "registration";
        }
        employeeService.saveAndFlush(employee);
        return "redirect:/registration?success";
        }

}

Employee Dto

@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class EmployeeDto
{
    private Long id;
    
    @NotEmpty
    private String firstName;
    
    @NotEmpty
    private String lastName;
    
    @NotEmpty(message = "Email should not be empty")
    @Email
    private String email;
    
    @NotEmpty(message = "Password should not be empty")
    private String password;
    
    @NotEmpty(message = "Department should not be empty")
    private String department;
    
    @NotEmpty(message = "Birth date should not be empty")
    private String birthDate;
    
    @NotEmpty(message = "Phone number should not be empty")
    private String phoneNumber;
}

Entities

@Getter
@Setter
@ToString
@Entity
@Table(name = "employees")
public class Employee {
 
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id; 
    
    @Column(name = "first_name", nullable = false)
    private String firstName;
    
    @Column(name = "last_name", nullable = false)
    private String lastName;
    
    @Column(name = "department", nullable = false)
    private String department;
    
    @Column(name = "birth_date", nullable = false)
    private String birthDate;
    
    @Column(name = "phone_number", nullable = false)
    private String phoneNumber;

    @Column(name = "email", nullable = false, unique = true)
    private String email;
    
    @Column(name = "password", nullable = false)
    private String password;

    @ManyToMany(fetch = FetchType.EAGER, cascade=CascadeType.ALL)
    @JoinTable(
            name="employees_roles",
            joinColumns={@JoinColumn(name="EMPLOYEE_ID", referencedColumnName="ID")},
            inverseJoinColumns={@JoinColumn(name="ROLE_ID", referencedColumnName="ID")})
    private List<Role> roles = new ArrayList<>();

}


@Setter
@Getter
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name="roles")
public class Role
{
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable=false, unique=true)
    private String name;

    @ManyToMany(mappedBy="roles")
    private List<Employee> employee;
}

Employee and role repositories

 @Repository
public interface EmployeeRepository extends JpaRepository<Employee, Long>{
    Optional<Employee> findByEmail(String email);
}


@Repository
public interface RoleRepository extends JpaRepository<Role, Long> {
    Role findByName(String name);
}

Employee Service

public interface EmployeeService {

    void saveAndFlush(EmployeeDto employeeDto);

    Optional<Employee>  findByEmail(String email);

    List<EmployeeDto> findAll();

}

Employee Service Implementation

@Service
public class EmployeeServiceImpl implements EmployeeService {
@Autowired
    private EmployeeRepository employeeRepository;
    private RoleRepository roleRepository;
    private PasswordEncoder passwordEncoder;
 
    @Override
    public void saveAndFlush(EmployeeDto employeeDto) {
        Employee employee = new Employee();
        employee.setEmail(employeeDto.getEmail());

        employee.setPassword(passwordEncoder.encode(employeeDto.getPassword()));
        Role role = roleRepository.findByName("ROLE_ADMIN");
        if(role == null){
            role = checkRoleExist();
        }
        employee.setRoles(Arrays.asList(role));
        employeeRepository.save(employee);
    }

    @Override
    public Optional<Employee> findByEmail(String email) {
        return employeeRepository.findByEmail(email);
    }

    @Override
    public List<EmployeeDto> findAll() {
        List<Employee> employees = employeeRepository.findAll();
        return employees.stream().map((employee) -> convertEntityToDto(employee))
                .collect(Collectors.toList());
    }

    private EmployeeDto convertEntityToDto(Employee employee){
        EmployeeDto employeeDto = new EmployeeDto();
        employeeDto.setEmail(employee.getEmail());
        return employeeDto;
    }

    private Role checkRoleExist() {
        Role role = new Role();
        role.setName("ROLE_ADMIN");
        return roleRepository.save(role);
    }

}

Employee Details Service Implementation

    @Service
public class EmployeeDetailsServiceImpl implements UserDetailsService {

    private EmployeeRepository employeeRepository;

    public EmployeeDetailsServiceImpl(EmployeeRepository employeeRepository) {
        this.employeeRepository = employeeRepository;
    }

    @SuppressWarnings("unchecked")
    @Override
    public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
        Employee employee = employeeRepository.findByEmail(email).orElseThrow(() ->
                new UsernameNotFoundException("User doesn't exists"));
        
        return new org.springframework.security.core.userdetails.User(
                employee.getEmail(), employee.getPassword(), (Collection<? extends GrantedAuthority>) Set.of(employee.getRoles())
        );
    }
    
}

HTML login and register forms

<body>
    <div >
        <div >
            <div >
                <div >
                    <div >Вхід</div>
                    <div th:if="${param.error}">
                        <div >Invalid Email or Password</div>
                    </div>
                    <form  method="post" role="form" th:action="@{/login}">
                        <div >
                            <div >
                                <i ></i>
                                <input type="text" placeholder="Введіть пошту" id="email" name="email" required/>
                            </div>
                            <div >
                                <i ></i>
                                <input type="password" placeholder="Введіть пароль" id="password" name="password" required/>
                            </div>
                            <div >
                                <input type="submit" value="Вхід">
                            </div>
                            <div >
                            <label>
                            <a href="@{/registration}">Зареєструватися</a>
                            </label>
                            </div>
                        </div>
                    </form>
                </div>
            </div>
        </div>
    </div>
</body>

</html>

Registration form

 <body>
        <div >
            <div >
                <div >
                    <div >
                        <div >Реєстрація</div>
                        <div th:if="${param.success}">
                        <div >You've successfully registered
                        to our app!</div>
                        </div>
                        <form method="post" role="form" th:action="@{/registration/save}" th:object="${user}">
                            <div >
                                <div >
                                    <i ></i>
                                    <input type="text" placeholder="Введіть ім'я" id="firstName" name="firstName" th:field="*{firstName}"/>
                                    <p th:errors="*{firstName}" 
                                        th:if="${#fields.hasErrors('firstName')}">
                                    </p>
                                </div>
                                <div >
                                    <i ></i>
                                    <input type="text" placeholder="Введіть прізвище" id="lastName" name="lastName" th:field="*{lastName}"/>
                                    <p th:errors="*{lastName}" 
                                        th:if="${#fields.hasErrors('lastName')}">
                                    </p>
                                </div>
                                <div >
                                    <i ></i>
                                    <input type="text" placeholder="Введіть пошту" id="email" name="email" th:field="*{email}"/>
                                 <!--   <p th:errors="*{email}" 
                                    th:if="${#fields.hasErrors('email')}">
                                    </p>
                                     -->
                                </div>
                                <div >
                                    <i ></i>
                                    <input type="password" placeholder="Введіть пароль" id="password" name="password" th:field="*{password}"/>
                                    <p th:errors="*{password}" 
                                    th:if="${#fields.hasErrors('password')}">
                                    </p>
                                </div>
                                <div >
                                    <i ></i>
                                    <input  type="text" placeholder="Введіть дату народження" id="birthDate" name="birthDate" th:field="*{birthDate}"/>
                                    <p th:errors="*{birthDate}" 
                                    th:if="${#fields.hasErrors('birthDate')}">
                                    </p>
                                </div>
                                <div >
                                    <i ></i>
                                    <input type="text" placeholder="Введіть підрозділ" id="department" name="department" th:field="*{department}"/>
                                    <p th:errors="*{department}" 
                                    th:if="${#fields.hasErrors('department')}">
                                    </p>
                                </div>
                                <div >
                                    <i ></i>
                                    <input type="text" placeholder="Введіть номер телефону" id="phoneNumber" name="phoneNumber" th:field="*{phoneNumber}"/>
                                    <p th:errors="*{phoneNumber}" 
                                    th:if="${#fields.hasErrors('phoneNumber')}">
                                    </p>
                                </div>
                                <div >
                                    <input type="submit" value="Зареєструватися">
                                </div>
                                <div ><label><a th:href="@{/login}">Увійти</a></label></div>
                            </div>
                        </form>
                    </div>
                </div>
            </div>
        </div>
    </body>
    
    </html>

CodePudding user response:

Where is the UserDetail implementation?

  • Make sure your EmployeeDto is correct. Also u have used one @Autowired for three objects in your EmployeeServiceImplementation. I am not sure that works. Try using @Autowired for each objects.

  • Its better to Autowire the employeeRepository object rather than using constructor in your EmployeeDetailsServiceImpl.

  • I'm still figuring out why your only password is getting null in table. I guess since only it is being passed through the passwordEncoder so might be something to do with that.

Hope this helps

  • Related