Error 403 when I submit the register form in spring boot project - spring

I am learning spring boot and write a register form, but when I run it in idea and submit the form, the browser occurs
There was an unexpected error (type=Forbidden, status=403).
Forbidden
I create the project by using spring initializr in idea, choose web+jpa+h2+thymeleaf.
I defined an Entity called Worker and set error messages in ValidationMessages.properties, here is the Worker entity
#Entity
public class Worker implements UserDetails {
private static final long serialversionUID = 1L;
#Id
#NotNull
#Size(min = 5, max = 16, message = "{username.size}")
private String username;
#NotNull
#Size(min = 2, max = 30, message = "{firstName.size}")
private String firstname;
#NotNull
#Size(min = 2, max = 30, message = "{lastName.size")
private String lastname;
#NotNull
#Size(min = 5, max = 25,message = "{password.size}")
private String password;
#NotNull
#Size(min = 2, max = 30, message = "{profession,size}")
private String profession;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getFirstname() {
return firstname;
}
public void setFirstname(String firstname) {
this.firstname = firstname;
}
public String getLastname() {
return lastname;
}
public void setLastname(String lastname) {
this.lastname = lastname;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getProfession() {
return profession;
}
public void setProfession(String profession) {
this.profession = profession;
}
//UserDetails methods
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return Arrays.asList(new SimpleGrantedAuthority("WORKER"));
}
#Override
public boolean isAccountNonExpired() {
return true;
}
#Override
public boolean isAccountNonLocked() {
return true;
}
#Override
public boolean isCredentialsNonExpired() {
return true;
}
#Override
public boolean isEnabled() {
return true;
}
}
and WorkersRepository
public interface WorkersRepository extends JpaRepository<Worker, String> {
Worker findByUsername(String username);
}
I have added spring security, and wrote the config:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private WorkersRepository workersRepository;
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/submit").access("hasRole('WORKER')")
.anyRequest().permitAll()
.and()
.formLogin()
.loginPage("/login")
.and()
.logout()
.logoutSuccessUrl("/")
.and()
.rememberMe()
.tokenValiditySeconds(4838400)
.key("workerKey");
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(new UserDetailsService() {
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
return workersRepository.findByUsername(username);
}
});
}
}
If the register input occurs error, the controller returns the registerForm.html to ask user to input again correctly. If the register has no error, the controller redirects to "/", a simple welcome.html. But whether the input is correct or not, I always get the error 403. When I inputhttp://localhost:8080/, I can get the welcome.html,which is a simple page with "welcome!" words. My controller is
private WorkersRepository workersRepository;
#Autowired
public WorkingHoursController(
WorkersRepository workersRepository) {
this.workersRepository = workersRepository;
}
#RequestMapping(method = RequestMethod.GET)
public String welcomePage() {
return "welcome";
}
#RequestMapping(value = "/register", method = RequestMethod.GET)
public String showRegistrationForm(Model model) {
model.addAttribute(new Worker());
return "registerForm";
}
#RequestMapping(value = "/register", method = RequestMethod.POST)
public String registrationProcessing(#Valid Worker worker, Errors errors, RedirectAttributes model) {
if(errors.hasErrors()) {
return "registerForm";
}
workersRepository.save(worker);
model.addAttribute("username", worker.getUsername());
model.addFlashAttribute("worker", worker);
return "redirect:/";
}
...
I wrote the registerForm.html using thymeleaf and add error validations. My registerForm.html is
<form class="form-signin" method="post" th:object="${worker}">
<div class="errors" th:if="${#fields.hasErrors('*')}">
<ul>
<li th:each="err : ${#fields.errors('*')}"
th:text="${err}">Input is in correct.</li>
</ul>
</div>
<img class="mb-4" src="https://getbootstrap.com/assets/brand/bootstrap-solid.svg" alt="" width="72" height="72">
<h1 class="h3 mb-3 font-weight-normal">Please register</h1>
<!-- input username -->
<label for="inputUsername" th:class="${#fields.hasErrors('username')}? 'error'">Username</label>
<input type="text" id="inputUsername" th:field="*{username}" th:class="${#fields.hasErrors('username')}? 'error form-control':'form-control'" placeholder="Username">
...
<!-- input password -->
<label for="inputPassword" th:class="${#fields.hasErrors('password')}? 'error'">Password</label>
<input type="password" id="inputPassword" th:field="*{password}" th:class="${#fields.hasErrors('password')}? 'error form-control':'form-control'" placeholder="Password">
<div class="checkbox mb-3">
<label>
<input type="checkbox" id="remember-me" name="remember-me"> Remember me
</label>
</div>
<button class="btn btn-lg btn-primary btn-block" type="submit">Register</button>
Before I add validations in thymeleaf and add spring security, everything seems to work properly.

You did not put any action inside form tag. Perhaps that's why you are getting error. Put action inside form tag like this one
<form class="form-signin" action="#" th:action="#{/register}" method="post" th:object="${worker}">

Please check once whether role should be "WORKER" or "ROLE_WORKER" according to your Spring Security JAR version. Also disable the CSRF in your application, and set global CORS config to accept all requests.

add the following to your config class
#Override
public void configure(HttpSecurity http) throws Exception {
http
.csrf()
.disable()
}
This is security stuff, it prevents hackers from using your open account credentials without your permission i.e. you get an email that say click here (that click takes your user info to another site without your consent). only use the above for dev.
For pro; add a csrf token as a hidden input on your form. this token is in your spring server.
<input type="hidden" name="_csrf" th:value="${_csrf.token}"/>

Related

Tomcat error in Spring Security (Spring + MySQL)

Why, when the admin wants to add a new admin, I get an error in the tomcat. I kind of wrote the code correctly, you can see probably made a mistake. I want the admin to be able to add a new user and give him a username, password, role (admin or user). And also that he stored information in a database MySQL. Problem on page "AddUser.JSP"
Tomcat Error
AdminController
#Controller
#RequestMapping("/admin")
public class AdminController {
#Autowired
private StudentService studentService;
private UserService userService;
#GetMapping("/allStudentsAdmin")
public ModelAndView allStudentsForUser() {
ModelAndView mv = new ModelAndView();
List<Student> studentList = studentService.getAllStudents();
mv.addObject("studentList", studentList);
mv.setViewName("allStudentsAdmin");
return mv;
}
#GetMapping(value = "/deleteStudent/{id}")
public ModelAndView deleteUserById(#PathVariable Long id) {
studentService.deleteStudentById(id);
ModelAndView mv = new ModelAndView("redirect:/admin/allStudentsAdmin");
return mv;
}
#GetMapping(value = "/editStudent/{id}")
public ModelAndView displayEditUserForm(#PathVariable Long id) {
ModelAndView mv = new ModelAndView("adminEditStudent");
Student student = studentService.getStudentById(id);
mv.addObject("headerMessage", "Редактирование студента");
mv.addObject("student", student);
return mv;
}
#PostMapping(value = "/editStudent")
public String saveEditedUser(
#RequestParam("id") Long id,
#RequestParam("name") String name,
#RequestParam("surname") String surname,
#RequestParam("avatar") MultipartFile file) {
try {
studentService.updateStudent(name, surname, file, studentService.getStudentById(id));
} catch (FileSystemException ex) {
ex.printStackTrace();
} catch (IOException e) {
return "redirect:/errors";
}
return "redirect:/admin/allStudentsAdmin";
}
#GetMapping(value = "/addStudentAdmin")
public ModelAndView displayNewUserForm() {
ModelAndView mv = new ModelAndView("addStudentAdmin");
mv.addObject("headerMessage", "Add Student Details");
mv.addObject("student", new Student());
return mv;
}
#PostMapping(value = "/addStudentAdmin")
public String saveNewStudent(#RequestParam("name") #NonNull String name,
#RequestParam("surname") #NonNull String surname,
#RequestParam("avatar") MultipartFile file)
throws IOException {
Student student = new Student();
student.setSurname(surname);
student.setName(name);
if (file != null && !file.isEmpty()) {
student.setAvatar(studentService.saveAvatarImage(file).getName());
}
studentService.saveStudent(student);
return "redirect:/admin/allStudentsAdmin";
}
#GetMapping(value = "/addUser")
public ModelAndView displayAddUserForm() {
ModelAndView mv = new ModelAndView("addUser");
mv.addObject("user", new User());
return mv;
}
#PostMapping(value = "/addUser")
public String saveNewUser(#RequestParam("login") #NonNull String login,
#RequestParam("password") #NonNull String password,
#RequestParam("role") #NonNull String role)
throws IOException {
User user = new User();
user.setPassword(password);
user.setLogin(login);
userService.saveUser(user);
return "redirect:/admin/allStudentsAdmin";
}
}
AddUser.JSP
<body>
<div class="add">
<br>
<br>
<br>
<br>
<center>
<form:form method="POST" action="${pageContext.request.contextPath}/admin/addUser" enctype="multipart/form-data">
<table>
<tr>
<td><label path="Login">Login</label></td>
<td><input type="text" name="login"/></td>
</tr>
<tr>
<td><label path="Password">Password</label></td>
<td><input type="text" name="password"/></td>
</tr>
<tr>
<td><select path="role" name="nubexSelect" size="3" multiple form="nubexForm">
<option>Admin</option>
<option>User</option>
</select></td>
<td><input class="btn btn-primary" type="submit" value="Submit"></td>
</tr>
</table>
</form:form>
</center>
</div>
</body>
User.JAVA
#Entity
#Table(name = "user")
public class User implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private String login;
private String password;
private String role;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getLogin() {
return login;
}
public void setLogin(String login) {
this.login = login;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getRole() {
return role;
}
public void setRole(String role) {
this.role = role;
}
#Override
public String toString() {
return "Student{" +
"id=" + id +
", login='" + login + '\'' +
", password='" + password + '\'' +
", role='" + role + '\'' +
'}';
}
}
UserRepository.JAVA
#Repository
public interface UserRepository extends CrudRepository<User, Long>{
}
UserService.JAVA
public interface UserService {
boolean saveUser(User user);
User updateUser(String login, String password, String role, User targetUser) throws IOException;
}
UserServiceImpl.java
#Service
#Transactional
public class UserServiceImpl implements UserService {
#Value("${storage.location}")
private String storageLocation;
private UserRepository repository;
public UserServiceImpl() {
}
#Autowired
public UserServiceImpl(UserRepository repository) {
super();
this.repository = repository;
}
#Override
public boolean saveUser(User user) {
try {
repository.save(user);
return true;
} catch (Exception ex) {
return false;
}
}
#Override
public User updateUser(String login, String password, String role, User targetUser)
throws IOException {
if (login != null && !login.equals(targetUser.getLogin())) {
targetUser.setLogin(login);
}
if (password != null && !password.equals(targetUser.getPassword())) {
targetUser.setPassword(password);
}
if (role != null && !role.equals(targetUser.getRole())) {
targetUser.setRole(role);
}
return targetUser;
}
SecurityConfig
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(final AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("admin").password(passwordEncoder().encode("1234")).roles("ADMIN")
.and()
.withUser("user").password(passwordEncoder().encode("user1234")).roles("USER")
.and();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/admin/**").hasRole("ADMIN")
.antMatchers("/user/**").hasRole("USER")
.antMatchers("/**").permitAll()
.and()
.formLogin()
.loginPage("/login")
.defaultSuccessUrl("/allStudents")
.and()
.logout()
.and()
.csrf().disable();
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
Your saveNewUser method is not having correct configuration
From JSP you are passing Form as multipart/form-data but in Controller class you have #RequestParam
Change method to User Object
#PostMapping(value = "/addUser", consumes = "multipart/form-data")
public String saveNewUser(#ModelAttribute User user)
throws IOException {
userService.saveUser(user);
return "redirect:/admin/allStudentsAdmin";
}
Also change the Role combo name parameter
<select path="role" name="role" size="3"

Login errors in spring boot: Encoded password does not look like BCrypt

I ran my spring boot project in idea, register, then I have the problem when I login. It redirected to the "localhost:8080/login?error" and has the following in idea console
Encoded password does not look like BCrypt
I've searched the answer in stackoverflow and saw this the link. But It seems to be useless to me because my configure extends WebSecurityConfigurerAdapter not AuthorizationServerConfigurerAdapter. My configure class is
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private WorkersRepository workersRepository;
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/submit").access("hasRole('WORKER')")
.anyRequest().permitAll()
.and()
.formLogin()
.loginPage("/login")
.and()
.logout()
.logoutSuccessUrl("/")
.and()
.rememberMe()
.tokenValiditySeconds(4838400)
.key("workerKey");
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(new UserDetailsService() {
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Worker worker = workersRepository.findByUsername(username);
return workersRepository.findByUsername(username);
}
}).passwordEncoder(new BCryptPasswordEncoder()).and()
.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
.withUser("admin").password("123456").roles("ADMIN","WORKER");
}
}
My Worker class is
#Entity
public class Worker implements UserDetails {
private static final long serialversionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#NotNull
#Size(min = 5, max = 16, message = "{username.size}")
private String username;
#NotNull
#Size(min = 2, max = 30, message = "{firstName.size}")
private String firstname;
#NotNull
#Size(min = 2, max = 30, message = "{lastName.size")
private String lastname;
#NotNull
#Size(min = 5, max = 25,message = "{password.size}")
private String password;
#NotNull
#Size(min = 2, max = 30, message = "{profession,size}")
private String profession;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getFirstname() {
return firstname;
}
public void setFirstname(String firstname) {
this.firstname = firstname;
}
public String getLastname() {
return lastname;
}
public void setLastname(String lastname) {
this.lastname = lastname;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getProfession() {
return profession;
}
public void setProfession(String profession) {
this.profession = profession;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
//UserDetails methods
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return Arrays.asList(new SimpleGrantedAuthority("WORKER"));
}
#Override
public boolean isAccountNonExpired() {
return true;
}
#Override
public boolean isAccountNonLocked() {
return true;
}
#Override
public boolean isCredentialsNonExpired() {
return true;
}
#Override
public boolean isEnabled() {
return true;
}
}
My WorkersRepository is
package com.rieunity.workinghours;
import org.springframework.data.jpa.repository.JpaRepository;
public interface WorkersRepository extends JpaRepository<Worker, String> {
Worker findByUsername(String username);
}
I did not login with user admin, I registered a new user in register.html and succeeded. Then I login in login page and failed with the error:
Encoded password does not look like BCrypt
Did I miss something?
I've solved the problem. Since I encoded the password by using BCryptPasswordEncoder, I should store the password in this form. Hence the solution is adding
String encodedPassword = new BCryptPasswordEncoder().encode(worker.getPassword());
worker.setPassword(encodedPassword);
into the "/register" post request
#RequestMapping(value = "/register", method = RequestMethod.POST)
public String registrationProcessing(#Valid Worker worker, Errors errors, RedirectAttributes model) {
if(errors.hasErrors()) {
return "registerForm";
};
String encodedPassword = new BCryptPasswordEncoder().encode(worker.getPassword());
worker.setPassword(encodedPassword);
workersRepository.save(worker);
model.addAttribute("username", worker.getUsername());
model.addFlashAttribute("worker", worker);
return "redirect:/";
}
The password field for a BCrypt should be at least 60 character because the result hash has 60 char.
#Size(min = 5, max = 60,message = "{password.size}")
private String password;
I had a same issue and solution is simple , first open online Bcrypt ecrypter site in browser :
https://www.dailycred.com/article/bcrypt-calculator
and first try in here.Java is use 12 number of rounds in Brcypt as default.You must use 12 ! not another number.

How can I display the current logged in User with Spring Boot Thymeleaf?

I am trying to display the details of the current user however I keep getting errors. I tried accessing the authenticated user from the template but that did not work as I was getting this error:
Method getFirstName() cannot be found on org.springframework.security.core.userdetails.User type
I was trying to get the information from a controller and then saving it in a string and passsing the string to a template but that wasn't working either.
Here is my SecurityConfig class:
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserService userService;
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers(
"/registration",
"/js/**",
"/css/**",
"/img/**",
"/webjars/**").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.logout()
.invalidateHttpSession(true)
.clearAuthentication(true)
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.logoutSuccessUrl("/login?logout")
.permitAll();
}
#Bean
public BCryptPasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
#Bean
public DaoAuthenticationProvider authenticationProvider(){
DaoAuthenticationProvider auth = new DaoAuthenticationProvider();
auth.setUserDetailsService(userService);
auth.setPasswordEncoder(passwordEncoder());
return auth;
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProvider());
}
Here is my UserService Class:
public interface UserService extends UserDetailsService {
User findByEmailAddress(String emailAddress);
// User findByFirstName(String firstName);
User save(UserRegistrationDto registration);
}
Here is my UserServiceImpl class:
#Service
public class UserServiceImpl implements UserService {
#Autowired
private UserRepository userRepository;
#Autowired
private BCryptPasswordEncoder passwordEncoder;
#Override
public UserDetails loadUserByUsername(String emailAddress) throws
UsernameNotFoundException {
User user = userRepository.findByEmailAddress(emailAddress);
if (user == null){
throw new UsernameNotFoundException("Invalid username or
password.");
}
return new
org.springframework.security.core.userdetails.User(user.getEmailAddress(),
user.getPassword(),
mapRolesToAuthorities(user.getRoles()));
}
public User findByEmailAddress(String emailAddress){
return userRepository.findByEmailAddress(emailAddress);
}
public User save(UserRegistrationDto registration){
User user = new User();
user.setFirstName(registration.getFirstName());
user.setSurname(registration.getSurname());
user.setEmailAddress(registration.getEmailAddress());
user.setPassword(passwordEncoder.encode(registration.getPassword()));
user.setRoles(Arrays.asList(new Role("ROLE_USER")));
return userRepository.save(user);
}
private Collection<? extends GrantedAuthority>
mapRolesToAuthorities(Collection<Role> roles){
return roles.stream()
.map(role -> new SimpleGrantedAuthority(role.getName()))
.collect(Collectors.toList());
}
}
Here is some code from the template class where I'm trying to get the information:
th:text ="${#authentication.getPrincipal().getFirstName()}">
th:text ="${#authentication.getPrincipal().getUser().getFirstName()}">
This is the login controller. The parts I have commented out was another way I was trying to get the current users details:
#Controller
//#RequestMapping("/login")
public class MainController {
// #GetMapping("/")
// public String root() {
// return "userProfile1";
// }
#GetMapping("/login")
public String login(Model model) {
return "login";
}
// #GetMapping
// public String displayUserAccount(#ModelAttribute("user") #Valid
UserRegistrationDto userDto, BindingResult result, Model model) {
//
//
// model.addAttribute("firstName", ((UserRegistrationDto)
auth).getEmailAddress());
//
// model.addAttribute("emailAddress", userDto.getEmailAddress());
// model.addAttribute("firstName", userDto.getFirstName());
// model.addAttribute("surname", userDto.getSurname());
// model.addAttribute("age", userDto.getAge());
// model.addAttribute("gender", userDto.getGender());
// model.addAttribute("dob", userDto.getDob());
// // return "redirect:/registration?success";
// return "userProfile1";
//
// }
#ResponseBody
public String currentUserName(Authentication auth) {
((UserRegistrationDto) auth).getEmailAddress();
return "userProfile1";
}
}
This is all over the place I'm sorry! Thanks so much for anyone who helps :D
You can use Thymeleaf extras for display authenticated user details.
Thymeleaf Extras Springsecurity4
<div th:text="${#authentication.name} ></div>
The problem is here:
return new
org.springframework.security.core.userdetails.User(user.getEmailAddress(),
user.getPassword(),
mapRolesToAuthorities(user.getRoles()));
You lose the reference to your User entity. Change it to:
return user;
For this to work, you need to update your User entity to implement UserDetails interface:
public class User implements UserDetails {
// some new methods to implement
}
Then, your Thymleaf code should work. Another way of getting the firstName would be:
<span th:text="${#request.userPrincipal.principal.firstName}"></span>
I figured out how to fix my problem.
I created this method in a controller:
#Autowired
UserRepository userR;
#GetMapping
public String currentUser(#ModelAttribute("user") #Valid UserRegistrationDto userDto, BindingResult result, Model model) {
Authentication loggedInUser = SecurityContextHolder.getContext().getAuthentication();
String email = loggedInUser.getName();
User user = userR.findByEmailAddress(email);
String firstname = user.getFirstName();
model.addAttribute("firstName", firstname);
model.addAttribute("emailAddress", email);
return "userProfile1"; //this is the name of my template
}
and then I added this line of code in my html template:
Email: th:text="${emailAddress}"
Reference (4. Spring Security Dialect):
https://www.thymeleaf.org/doc/articles/springsecurity.html
Add dependencies pom.xml
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity4</artifactId>
</dependency>
and the view (Thymeleaf):
<div sec:authorize="isAuthenticated()">
Authenticated user roles:
Logged user: <span sec:authentication="name"></span> |
Roles: <span sec:authentication="principal.authorities"></span>
</div>
I hope you serve them
You can get the username attribute easily from your Principal class.
#GetMapping(value = "/")
public String index(#AuthenticationPrincipal MyUserPrincipal principal) {
String username = principal.getUsername();
//Do whatever you want here
return "index";
}
However, if you want more details than the ones inside Principal class then you need to explicitly define them in your principal class:
public int getId() {
return member.getId();
}
So now you can invoke it directly:
#GetMapping(value = "/")
public String index(#AuthenticationPrincipal MyUserPrincipal principal) {
int userId = principal.getId();
//Do whatever you want here
return "index";
}
You will need to import the following:
import org.springframework.security.core.annotation.AuthenticationPrincipal;
If you only want to get a Principal class attribute directly from Thymeleaf then you can alternatively do the following:
<span sec:authentication="principal.username">Username</span>

Spring boot security custom messages while user login

I am trying to integrate spring security in my spring boot application.All working ok but how do I display a message if the account is expired or account is locked? Also, I do not want to display error message based on parm like http://localhost:8080/login?error
Here is my current code: login.html
<div th:if="${param.error}" class="alert alert-danger">
Invalid username or password.
</div>
<h3>Sign in to continue</h3>
<form th:action="#{/login}" name="loginForm" method="POST">
<div class="form-group">
<label for="userNameInput">Username</label>
<input type="text" class="form-control" id="userNameInput" name="username" placeholder="Username" />
</div>
<div class="form-group">
<label for="passwordInput">Password</label>
<input type="password" class="form-control" id="passwordInput" name="password" placeholder="Password" />
</div>
<button type="submit" class="btn btn-success">Login</button>
</form>
WebSecurityConfig.java
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private CustomUserDetailsService customUserDetailsService;
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/", "/css/**", "/js/**","/login/**").permitAll()
.anyRequest().authenticated()
.and().formLogin().loginPage("/login").defaultSuccessUrl("/dashboard")
.and().logout().logoutSuccessUrl("/");
}
#Autowired
public void configAuthBuilder(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(customUserDetailsService).passwordEncoder(new BCryptPasswordEncoder());
}
}
CustomUserDetailsService.java
#Service
public class CustomUserDetailsService implements UserDetailsService {
#Autowired
UserRepository userRepository;
#Override
#Transactional(readOnly = true)
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
//query for user from DB
User user = userRepository.findByUsername(username);
if (user == null) {
throw new UsernameNotFoundException(username);
}
Date today = new Date();
//do check if account expired / suspended / deleted
Boolean isAcconutExpired = false;
Boolean status = false;
if (user.getExpireOn() != null && today.before(user.getExpireOn())) {
isAcconutExpired = false;
}
if(user.getStatus() == 1){
status = true;
}
return new org.springframework.security.core.userdetails.User(user.getUsername(),
user.getPassword(),
status,
!isAcconutExpired,
true,
true,
getAuthorities(user));
}
private List<GrantedAuthority> getAuthorities(User user) {
List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
authorities.add(new SimpleGrantedAuthority("USER"));
return authorities;
}
}
UserRepository.java
#Repository
public interface UserRepository extends CrudRepository<User, Long> {
User findByUsername(String username);
}
This message is a bit old, but I'm currently facing the same problem.
So, firstly, you have to create a custom instance of Authentication Provider to let HideUserNotFoundExceptions be passed to the controler:
public AuthenticationProvider daoAuthenticationProvider() {
DaoAuthenticationProvider impl = new DaoAuthenticationProvider();
impl.setUserDetailsService(customUserDetailsService);
impl.setPasswordEncoder(new BCryptPasswordEncoder());
impl.setHideUserNotFoundExceptions(false) ;
return impl ;
}
Moreover, you should add this provider in the AuthenticationProviderBuilder, instead of adding customDetailService (adding customDetailService will add an other provider) :
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(daoAuthenticationProvider());
}
With this, you can now catch UserNotFoundException instead of basic BadCredentialsException .
So, it remains to display custom error message for these two exceptions. The Badcredentials exception is throw directly by SpringSecurity with an error message based on I18n message AbstractUserDetailsAuthenticationProvider.badCredentials (see the implementation in spring security here ):
throw new BadCredentialsException(this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
Same for expiration and account locked.
So I suggest you to change your CustomUserDetailsService.java class to do the same with a similar code error :
if (user == null) {
throw new UsernameNotFoundException(SpringSecurityMessageSource.getAccessor().getMessage("AbstractUserDetailsAuthenticationProvider.UserUnknown",new Object[] {login},"User is not known"));
}
After this, you can add these lines in your message.properties :
AbstractUserDetailsAuthenticationProvider.UserUnknown = {0} was not found.
AbstractUserDetailsAuthenticationProvider.badCredentials = Password is bad
AbstractUserDetailsAuthenticationProvider.credentialsExpired = User credentials have expired
AbstractUserDetailsAuthenticationProvider.disabled = User is disabled
AbstractUserDetailsAuthenticationProvider.expired = User account has expired
AbstractUserDetailsAuthenticationProvider.locked = User account is locked
And display error message in login.html :
<div class="dialog-row">
<label th:if="${param.error}" th:text="${session['SPRING_SECURITY_LAST_EXCEPTION'].message}" class="text-center redText">Mot de passe inconnu</label>
</div>

Spring Authentication failure

I would like to ask from some insight as I'm not sure what is wrong here. I need to add authentication via database stored details so I'm trying to do that. The problem being that every userdetails I use to access the main pages returns the login failed page.
The DAO layer
#Transactional
#Repository ("staffDAO")
public class StaffDAO extends AbstractDAO<Staff>{
public StaffDAO() {
super(Staff.class);
}
#Autowired
#Resource(name="sessionFactory")
private SessionFactory sessionFactory;
private Transaction transaction;
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;}
#SuppressWarnings("unchecked")
public List<Authority> getAuthority() {
Session session = sessionFactory.getCurrentSession();
Query query = session.createQuery("from Authority ");
return query.list();}
public void saveAuthority(Authority authority) {
try {
Session session = sessionFactory.getCurrentSession();
transaction = session.beginTransaction();
session.save(authority);
transaction.commit();
} catch (Exception e) {
transaction.rollback();}}
#Transactional("hibernatetransactionManager")
public Staff getUserByUserName(String userName){
Query queryResult;
queryResult =getCurrentSession().createQuery("from Staff where username=:userName");
queryResult.setParameter("userName", new String(userName));
return (Staff) queryResult.list().get(0)}}
Service layers
public interface StaffService{
List<Authority> getAuthorities();
public void saveAuthority(Authority authority);
public Staff getUserByUserName(String userName);}
#Service("staffService")
#Transactional(propagation = Propagation.REQUIRED, readOnly = true)
public class StaffServiceImpl implements StaffService {
#Autowired
StaffDAO staffDAO;
private AbstractDAO<Staff> sessionFactory;
public StaffDAO getStaffDAO() {
return staffDAO;}
public void setStaffDAO(StaffDAO staffDAO) {
this.staffDAO = staffDAO;}
#Override
public List<Authority> getAuthorities() {
return staffDAO.getAuthority();}
#Override
public void saveAuthority(Authority authority) {
staffDAO.saveAuthority(authority);}
#Override
public Staff getUserByUserName(String userName) {
return staffDAO.getUserByUserName(userName);}}
CustomUserDetailsService
#Repository("customUserDetailsService")
#Service
#Component
public class CustomUserDetailsService implements UserDetailsService{
#Resource
private StaffService staffService;
public CustomUserDetailsService(){
}
public CustomUserDetailsService(StaffService staffService) {
this.staffService = staffService; }
#Override
public UserDetails loadUserByUsername(String userName)
throws UsernameNotFoundException {
Staff staff;
try {
staff = staffService.getUserByUserName(userName);
} catch (Exception e) {
throw new UsernameNotFoundException(
"getUserByUserName returned null.");}
return (UserDetails) staff;}}
Models
#Entity
#Table(name = "staff")
#Component
public class Staff implements Serializable, UserDetails{
private static final long serialVersionUID = 8825646974241476909L;
#Id
#Column(name = "staff_id")
private String staffId;
#Column(name = "name")
private String name;
#Column(name = "username")
private String username;
#Column(name = "password")
private String password;
***** getters and setters
public void setAuthorities(Set<Authority> authorities) {
this.authorities = authorities;
}
#Override
public Set<Authority> getAuthorities() {
return authorities;
}}
#Entity
#Table(name="authorities")
#Component
public class Authority implements Serializable, GrantedAuthority{
private static final long serialVersionUID = 1L;
public Authority(){
// must have one default constructor
}
#Id
#Column(name = "authority")
private String authority;
#Column(name = "role_name",nullable=false,unique=true)
private String roleName;
public String getRoleName() {
return roleName;
}
public void setRoleName(String roleName) {
this.roleName = roleName;
}
#Override
public String getAuthority() {
return authority;
}
public void setAuthority(String authority) {
this.authority = authority;
}
public Authority(String authority,String roleName){
this.authority=authority;
this.roleName=roleName;
}}
XML config
<authentication-manager>
<authentication-provider user-service-ref="customUserDetailsService"/>
</authentication-manager>
<beans:bean id="customUserDetailsService"
class="com.project.professional.service.CustomUserDetailsService">
</beans:bean>
<http auto-config="true" use-expressions="true" >
<intercept-url pattern="/j_spring_security_check" access="permitAll"/>
<intercept-url pattern='/home' access="hasRole('ROLE_USER')" />
<form-login login-page='/login' always-use-default-target="true" default-target-
url="/home" authentication-failure-url="/auth/loginFailed"/>
<logout invalidate-session="true" logout-success-url='/login' />
</http>
Controller
#RequestMapping(value="/login", method = RequestMethod.GET)
public String getLoginPage(ModelMap model) {
return "login";}
#RequestMapping(value = "/home", method = RequestMethod.GET)
public String getHomePage(Locale locale, Model model) {
return "/home";}
Jsp
<c:url value="/j_spring_security_check" var="loginUrl"/>
<form action="${loginUrl}" method="post" name="loginForm">
<p>
<label for="j_username">Username</label>
<input id="j_username" name="j_username" type="text" />
</p>
<p>
<label for="j_password">Password</label>
<input id="j_password" name="j_password" type="password" />
</p>
<input type="submit"
value="Login"/>
</form>
So what I'm asking is if there is anything that I'm missing, as to why it does not authenticate and return the home page. I would appreciate any insight into this.

Resources