Have a good day.
I am facing an issue when verifying the user osamazaza200 with the correct password (test123) during login in my Spring Boot application. Despite providing the proper credentials, the authentication process fails, and a 403 error is returned.
Steps to Reproduce
Create a user with the following credentials:
Username: osamazaza200
Password: test123 (hashed and stored in the database)
Attempt to sign in using the /signin endpoint with the credentials.
Observe the response, which results in a 403 Forbidden error.
Relevant Code:Fristly: Security Config Class:
@Configuration@EnableMethodSecuritypublic class SecurityConfig { private UserDetailsService userDetailsService; public SecurityConfig(UserDetailsService userDetailsService){ this.userDetailsService = userDetailsService; } @Bean public static PasswordEncoder passwordEncoder(){ return new BCryptPasswordEncoder(); } @Bean public AuthenticationManager authenticationManager( AuthenticationConfiguration configuration) throws Exception { return configuration.getAuthenticationManager(); } @Bean SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http.authorizeHttpRequests((authorize) -> //authorize.anyRequest().authenticated() authorize.requestMatchers(HttpMethod.GET, "/api/**").permitAll() .requestMatchers("/api/auth/**").permitAll() .anyRequest().authenticated() ); return http.build(); }}
Secondly: AuthController
package com.muhammedessa.securityapicrud.controllers;@RestController@RequestMapping("/api/auth")public class AuthController { @Autowired private AuthenticationManager authenticationManager; @Autowired private UserRepository userRepository; @Autowired private RoleRepository roleRepository; @Autowired private PasswordEncoder passwordEncoder; @PostMapping("/signin") public ResponseEntity<String> authenticateUser(@RequestBody LoginDto loginDto){ Authentication authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken( loginDto.getUsernameOrEmail(), loginDto.getPassword())); SecurityContextHolder.getContext().setAuthentication(authentication); return new ResponseEntity<>("User signed-in successfully!.", HttpStatus.OK); } @PostMapping("/signup") public ResponseEntity<?> registerUser(@RequestBody SignUpDto signUpDto){ // add check for username exists in a DB if(userRepository.existsByUsername(signUpDto.getUsername())){ return new ResponseEntity<>("Username is already taken!", HttpStatus.BAD_REQUEST); } // add check for email exists in DB if(userRepository.existsByEmail(signUpDto.getEmail())){ return new ResponseEntity<>("Email is already taken!", HttpStatus.BAD_REQUEST); } // create user object User user = new User(); user.setName(signUpDto.getName()); user.setUsername(signUpDto.getUsername()); user.setEmail(signUpDto.getEmail()); user.setPassword(passwordEncoder.encode(signUpDto.getPassword())); Role roles = roleRepository.findByName("ROLE_ADMIN").get(); user.setRoles(Collections.singleton(roles)); userRepository.save(user); return new ResponseEntity<>("User registered successfully", HttpStatus.OK); }}
Thirdly: Login DTO
package com.muhammedessa.securityapicrud.dto;import lombok.Data;@Datapublic class LoginDto { private String usernameOrEmail; private String password;}
Fourthly: Sign UP DTO
package com.muhammedessa.securityapicrud.dto;import lombok.Data;@Datapublic class SignUpDto { private String name; private String username; private String email; private String password;}
Fifthly: Role Class
package com.muhammedessa.securityapicrud.entity;import jakarta.persistence.Column;import jakarta.persistence.Entity;import jakarta.persistence.GeneratedValue;import jakarta.persistence.GenerationType;import jakarta.persistence.Id;import jakarta.persistence.Table;import lombok.Getter;import lombok.Setter;@Setter@Getter@Entity@Table(name = "roles")public class Role { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private long id; @Column(length = 60) private String name;}
Sixthly: User Role Class
package com.muhammedessa.securityapicrud.entity;import java.util.Set;import jakarta.persistence.*; import lombok.Data;@Data@Entity@Table(name = "users", uniqueConstraints = { @UniqueConstraint(columnNames = {"username"}), @UniqueConstraint(columnNames = {"email"})})public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private long id; private String name; private String username; private String email; private String password; @ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL) @JoinTable(name = "user_roles", joinColumns = @JoinColumn(name = "user_id", referencedColumnName = "id"), inverseJoinColumns = @JoinColumn(name = "role_id", referencedColumnName = "id")) private Set<Role> roles;}
Seventhly: Role Repository Class
package com.muhammedessa.securityapicrud.repository;import java.util.Optional;import org.springframework.data.jpa.repository.JpaRepository;import com.muhammedessa.securityapicrud.entity.Role;public interface RoleRepository extends JpaRepository<Role, Long> { Optional<Role> findByName(String name);}
Eighty: User Repository Class
package com.muhammedessa.securityapicrud.repository;import java.util.Optional;import org.springframework.data.jpa.repository.JpaRepository;import com.muhammedessa.securityapicrud.entity.User;public interface UserRepository extends JpaRepository<User, Long> { Optional<User> findByEmail(String email); Optional<User> findByUsernameOrEmail(String username, String email); Optional<User> findByUsername(String username); Boolean existsByUsername(String username); Boolean existsByEmail(String email);}
Ninthly: CustomUserDetailsService
package com.muhammedessa.securityapicrud.services;import java.util.Set;import java.util.stream.Collectors;import org.springframework.security.core.authority.SimpleGrantedAuthority;import org.springframework.security.core.GrantedAuthority;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 com.muhammedessa.securityapicrud.entity.User;import com.muhammedessa.securityapicrud.repository.UserRepository;@Servicepublic class CustomUserDetailsService implements UserDetailsService { private UserRepository userRepository; public CustomUserDetailsService(UserRepository userRepository) { this.userRepository = userRepository; } @Override public UserDetails loadUserByUsername(String usernameOrEmail) throws UsernameNotFoundException { User user = userRepository.findByUsernameOrEmail(usernameOrEmail, usernameOrEmail) .orElseThrow(() -> new UsernameNotFoundException("User not found with username or email: "+ usernameOrEmail)); Set<GrantedAuthority> authorities = user .getRoles() .stream() .map((role) -> new SimpleGrantedAuthority(role.getName())).collect(Collectors.toSet()); return new org.springframework.security.core.userdetails.User(user.getEmail(), user.getPassword(), authorities); }}
Application Properties
spring.application.name=APIDemo# DATASOURCE (DataSourceAutoConfiguration & DataSourceProperties)spring.datasource.url=jdbc:mysql://127.0.0.1:3306/usersdb?useSSL=falsespring.datasource.username=rootspring.datasource.password= # Hibernate# The SQL dialect makes Hibernate generate better SQL for the chosen databasespring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQLDialect# Hibernate ddl auto (create, create-drop, validate, update)spring.jpa.hibernate.ddl-auto = updatelogging.level.org.hibernate.SQL=DEBUGlogging.level.org.hibernate.type=TRACE
When running [POST] request API http://localhost:8080/api/auth/signup
Body
{"name": "Loyasl Essa","username": "testrer","email": "test123@gmail.com","password": "12345678"}
getting 403 error