Spring Security: Finding User For Authentication With Hibernate and Spring Data JPA
I'm a beginner with Spring Boot/Security. I'm having trouble getting a user from MySQL db for authentication with my custom login page.
I have configured my custom UserDetailsService class, and I'm pretty sure my mapping with my SecurityConfig class works, but everytime I try logging in with an existing user in my database, with of the DEBUG logging errors always says "cannot find user ' '. This is the DEBUG message:
Hibernate: select u1_0.id,u1_0.email,u1_0.full_name,u1_0.password from user_info_table u1_0 where u1_0.email=?
2024-04-08T17:36:56.795-04:00 DEBUG 28416 --- [Finance_Tracker] [io-8080-exec-10] o.s.s.a.dao.DaoAuthenticationProvider : Failed to find user ''
It always brings me back to my login.html page since it never authenticates, but I want it to redirect to my dashboard page. Please help, I'd really appreciate it.Thanks!
SECURITY-CONFIG-CLASS:
@EnableWebSecurity
@Configuration
public class SecurityConfig {
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public UserDetailsService userDetailsService() {
return new UserInfoUserDetailsService();
}
@Bean
public AuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider dao = new DaoAuthenticationProvider();
dao.setUserDetailsService(userDetailsService());
dao.setPasswordEncoder(passwordEncoder());
return dao;
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf(AbstractHttpConfigurer::disable)
.authorizeHttpRequests( authorization -> authorization
.requestMatchers("/welcome", "/register", "/login").permitAll()
.requestMatchers("/welcome/**").authenticated()
)
.formLogin( login -> login
.loginPage("/login")
.defaultSuccessUrl("/dashboard", true)
.permitAll()
);
return http.build();
}
}
CUSTOM USER-DETAILS-SERVICE-CLASS
@Component
public class UserInfoUserDetailsService implements UserDetailsService {
@Autowired
private UserRepository userRepository;
private static final Logger logger = LoggerFactory.getLogger(UserInfoUserDetailsService.class);
@Override
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
logger.debug("Attempting to load user by email: {}", email);
User user = userRepository.findByEmail(email)
.orElseThrow(() -> new UsernameNotFoundException("No user found with email: " + email));
return new org.springframework.security.core.userdetails.User(user.getEmail(), user.getPassword(),
Collections.singletonList(new SimpleGrantedAuthority("ROLE_USER")));
}
}
USER MODEL CLASS:
@AllArgsConstructor
@NoArgsConstructor
@Data
@Entity
@Table(name = "user_info_table")
public class User {
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Id
private Long id;
@Column(name = "full_name")
private String fullName;
@Column(name = "email")
private String email;
@Column(name = "password")
private String password;
}
USER CONTROLLER
@Controller
public class UserController {
private UserService userService;
public UserController(UserService userService) {
this.userService = userService;
}
@GetMapping("/welcome")
public String displayWelcomePage() {
return "welcome";
}
@GetMapping("/register")
public String displayRegistrationPage() {
return "registration";
}
@PostMapping("/register")
public String registerUserToDatabase(@ModelAttribute User user,
RedirectAttributes redirect) {
userService.saveUser(user);
redirect.addFlashAttribute("successMessage", "Successful Registration!");
return "redirect:/login";
}
@GetMapping("/login")
public String loginPage() {
return "login";
}
@GetMapping("/dashboard")
public String displayDashboardPage() {
return "dashboard";
}
}
04-09T14:40:45.533-04:00 DEBUG 21460 --- [Finance_Tracker] [nio-8080-exec-7] o.s.s.w.a.AnonymousAuthenticationFilter : Set SecurityContextHolder to anonymous SecurityContext
2024-04-09T14:40:54.251-04:00 DEBUG 21460 --- [Finance_Tracker] [nio-8080-exec-8] o.s.security.web.FilterChainProxy : Securing POST /login
Hibernate: select u1_0.id,u1_0.email,u1_0.full_name,u1_0.password from user_info_table u1_0 where u1_0.email=?
2024-04-09T14:40:55.090-04:00 DEBUG 21460 --- [Finance_Tracker] [nio-8080-exec-8] o.s.s.a.dao.DaoAuthenticationProvider : Failed to find user ''
2024-04-09T14:40:55.090-04:00 DEBUG 21460 --- [Finance_Tracker] [nio-8080-exec-8] o.s.s.web.DefaultRedirectStrategy : Redirecting to /login?error
2024-04-09T14:40:55.102-04:00 DEBUG 21460 --- [Finance_Tracker] [nio-8080-exec-9] o.s.security.web.FilterChainProxy : Securing GET /login?error
2024-04-09T14:40:55.102-04:00 DEBUG 21460 --- [Finance_Tracker] [nio-8080-exec-9] o.s.security.web.FilterChainProxy : Secured GET /login?error
2024-04-09T14:40:55.102-04:00 DEBUG 21460 --- [Finance_Tracker] [nio-8080-exec-9] o.s.web.servlet.DispatcherServlet : GET "/login?error", parameters={masked}
2024-04-09T14:40:55.102-04:00 DEBUG 21460 --- [Finance_Tracker] [nio-8080-exec-9] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to com.diazdevin.finance_tracker.controller.UserController#loginPage()
2024-04-09T14:40:55.102-04:00 DEBUG 21460 --- [Finance_Tracker] [nio-8080-exec-9] o.s.w.s.v.ContentNegotiatingViewResolver : Selected 'text/html' given [text/html, application/xhtml+xml, image/avif, image/webp, image/apng, application/xml;q=0.9, */*;q=0.8]
2024-04-09T14:40:55.106-04:00 DEBUG 21460 --- [Finance_Tracker] [nio-8080-exec-9] o.s.web.servlet.DispatcherServlet : Completed 200 OK
2024-04-09T14:40:55.106-04:00 DEBUG 21460 --- [Finance_Tracker] [nio-8080-exec-9] o.s.s.w.a.AnonymousAuthenticationFilter : Set SecurityContextHolder to anonymous SecurityContext
2024-04-09T14:40:55.200-04:00 DEBUG 21460 --- [Finance_Tracker] [io-8080-exec-10] o.s.security.web.FilterChainProxy : Securing GET /favicon.ico
2024-04-09T14:40:55.205-04:00 DEBUG 21460 --- [Finance_Tracker] [io-8080-exec-10] o.s.s.w.a.AnonymousAuthenticationFilter : Set SecurityContextHolder to anonymous SecurityContext
2024-04-09T14:40:55.206-04:00 DEBUG 21460 --- [Finance_Tracker] [io-8080-exec-10] o.s.s.web.DefaultRedirectStrategy : Redirecting to http://localhost:8080/login
2024-04-09T14:40:55.215-04:00 DEBUG 21460 --- [Finance_Tracker] [nio-8080-exec-1] o.s.security.web.FilterChainProxy : Securing GET /login
2024-04-09T14:40:55.215-04:00 DEBUG 21460 --- [Finance_Tracker] [nio-8080-exec-1] o.s.security.web.FilterChainProxy : Secured GET /login
2024-04-09T14:40:55.215-04:00 DEBUG 21460 --- [Finance_Tracker] [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : GET "/login", parameters={}
2024-04-09T14:40:55.215-04:00 DEBUG 21460 --- [Finance_Tracker] [nio-8080-exec-1] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to com.diazdevin.finance_tracker.controller.UserController#loginPage()
2024-04-09T14:40:55.215-04:00 DEBUG 21460 --- [Finance_Tracker] [nio-8080-exec-1] o.s.w.s.v.ContentNegotiatingViewResolver : Selected '*/*' given [image/avif, image/webp, image/apng, image/svg+xml, image/*, */*;q=0.8]
2024-04-09T14:40:55.215-04:00 DEBUG 21460 --- [Finance_Tracker] [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Completed 200 OK
2024-04-09T14:40:55.215-04:00 DEBUG 21460 --- [Finance_Tracker] [nio-8080-exec-1] o.s.s.w.a.AnonymousAuthenticationFilter : Set SecurityContextHolder to anonymous SecurityContext
VIEW OF LOGIN PAGE
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Login Form</title>
</head>
<body>
<div th:if="${successMessage}">
<p th:text="${successMessage}" style="color:green"></p>
</div>
<form th:action="@{/login}" method="get">
<input type="text" name="email" placeholder="ENTER EMAIL: : " required>
<input type="text" name="password" placeholder="ENTER PASSWORD: " required>
<button type="submit">LOGIN</button>
</form>
</body>
</html>
NEW LOGS
2024-04-09T18:02:12.054-04:00 DEBUG 29216 --- [Finance_Tracker] [nio-8080-exec-8] o.s.s.w.a.AnonymousAuthenticationFilter : Set SecurityContextHolder to anonymous SecurityContext
2024-04-09T18:02:18.959-04:00 DEBUG 29216 --- [Finance_Tracker] [nio-8080-exec-3] o.s.security.web.FilterChainProxy : Securing GET /login?email=steve7%40gmail.com&password=apple
2024-04-09T18:02:18.972-04:00 DEBUG 29216 --- [Finance_Tracker] [nio-8080-exec-3] o.s.security.web.FilterChainProxy : Secured GET /login?email=steve7%40gmail.com&password=apple
2024-04-09T18:02:18.980-04:00 DEBUG 29216 --- [Finance_Tracker] [nio-8080-exec-3] o.s.web.servlet.DispatcherServlet : GET "/login?email=steve7%40gmail.com&password=apple", parameters={masked}
2024-04-09T18:02:18.980-04:00 DEBUG 29216 --- [Finance_Tracker] [nio-8080-exec-3] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to com.diazdevin.finance_tracker.controller.UserController#loginPage()
2024-04-09T18:02:18.980-04:00 DEBUG 29216 --- [Finance_Tracker] [nio-8080-exec-3] o.s.w.s.v.ContentNegotiatingViewResolver : Selected 'text/html' given [text/html, application/xhtml+xml, image/avif, image/webp, image/apng, application/xml;q=0.9, */*;q=0.8]
2024-04-09T18:02:18.980-04:00 DEBUG 29216 --- [Finance_Tracker] [nio-8080-exec-3] o.s.web.servlet.DispatcherServlet : Completed 200 OK
2024-04-09T18:02:18.980-04:00 DEBUG 29216 --- [Finance_Tracker] [nio-8080-exec-3] o.s.s.w.a.AnonymousAuthenticationFilter : Set SecurityContextHolder to anonymous SecurityContext
2024-04-09T18:02:19.048-04:00 DEBUG 29216 --- [Finance_Tracker] [nio-8080-exec-2] o.s.security.web.FilterChainProxy : Securing GET /favicon.ico
2024-04-09T18:02:19.048-04:00 DEBUG 29216 --- [Finance_Tracker] [nio-8080-exec-2] o.s.s.w.a.AnonymousAuthenticationFilter : Set SecurityContextHolder to anonymous SecurityContext
2024-04-09T18:02:19.048-04:00 DEBUG 29216 --- [Finance_Tracker] [nio-8080-exec-2] o.s.s.web.DefaultRedirectStrategy : Redirecting to http://localhost:8080/login
Answers
It seems like the user is not being authenticated correctly, even though you are fetching the user details from the database. This could be due to the way you are handling the login request and passing the email and password parameters.
In your HTML form, you are using the GET method to send the email and password as URL parameters:
<form th:action="@{/login}" method="get">
<input type="text" name="email" placeholder="ENTER EMAIL: " required>
<input type="text" name="password" placeholder="ENTER PASSWORD: " required>
<button type="submit">LOGIN</button>
</form>
Using the GET method to submit sensitive data like passwords is not recommended because it exposes the data in the URL, which can be intercepted and read. Instead, you should use the POST method for submitting the login form.
Change your form to use the POST method:
<form th:action="@{/login}" method="post">
<input type="text" name="email" placeholder="ENTER EMAIL: " required>
<input type="password" name="password" placeholder="ENTER PASSWORD: " required>
<button type="submit">LOGIN</button>
</form>
Notice that I also changed the input type for the password field to "password" to hide the entered characters.
In your controller, make sure you are correctly extracting the email and password parameters from the request body when handling the login request:
@PostMapping("/login")
public String loginPage(@RequestParam String email, @RequestParam String password, Model model) {
// Authenticate user and redirect based on authentication result
// You may need to use Spring Security for user authentication
}
Replace the @RequestParam
annotations with appropriate parameter names and types if necessary.
Additionally, ensure that your authentication mechanism is properly configured in your application. You might need to use Spring Security to handle user authentication and authorization. You can configure Spring Security to use your custom UserDetailsService
for authenticating users.
Finally, always ensure that you handle user passwords securely by hashing them before storing them in the database and comparing hashed passwords during authentication. Never store passwords in plain text.