I have a spring boot project with a /api/users/register and /api/users/login endpoint, the register endpoint works fine and allows me to POST to create a new user (With validation etc) but the login endpoint gives me a 401 (Unauthorized) response when I try to login with correct details.
Also, I am trying to add new roles to users as they are created but since I'm using JWT, I am unsure how to go about doing this, every tutorial online has a different implementation and I'm struggling to understand this too.
If someone can provide me on some steps to do this I'd appreciate it. I know you need to add the roles as claims when generating the token itself, but where do I need to implement the actual logic of assigning a new role upon creation of an account.
User.java
#Entity
#Table(name="user")
public class User{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(unique = true)
#NotBlank(message = "Username is required")
private String username;
#NotBlank(message = "Password is required")
private String password;
#Transient
private String confirmPassword;
private boolean enabled;
#JsonFormat(pattern = "yyyy-mm-dd")
private Date createdAt;
// I want to load all the roles of users + the user itself once requested for
#ManyToMany(fetch=FetchType.EAGER, cascade = CascadeType.ALL)
#JoinTable(name="user_roles",
joinColumns = {
#JoinColumn(name="user_id")
},
inverseJoinColumns = {
#JoinColumn(name="role_id")
})
private Set<Role> roles = new HashSet<>();
// no-arg and all arg constructor
// getters & setters
Role.java
#Entity
#Table(name="role")
public class Role {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String description;
// constructors
// getters & setters
** I do have repository/dao classes for both entities with a method to find user/role by name**
UserService.java
#Service
public class UserService {
#Autowired
private UserRepository userRepository;
#Autowired
private BCryptPasswordEncoder passwordEncoder;
#Autowired
private RoleService roleService;
public User saveUser(User user) {
try {
user.setPassword(passwordEncoder.encode(user.getPassword()));
user.setUsername(user.getUsername());
user.setConfirmPassword("");
user.setEnabled(true);
return userRepository.save(user);
} catch(Exception e) {
throw new UsernameAlreadyExistsException("User with username " + user.getUsername() + " already exists!");
}
}
}
RoleService.java
#Service
public class RoleService {
#Autowired
private RoleRepository roleRepository;
public Role findByRoleName(String roleName) {
Role theRole = roleRepository.findByName(roleName);
return theRole;
}
}
UserDetailsServiceImpl.java
#Service
public class UserDetailsServiceImpl implements UserDetailsService {
#Autowired
private UserRepository userRepository;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userRepository.findByUsername(username);
if(user == null) {
throw new UsernameNotFoundException("User not found");
}
return (UserDetails) user;
}
}
These are my JWT classes
**JwtAuthenticationFilter.java
public class JwtAuthenticationFilter extends OncePerRequestFilter {
#Autowired
private JwtTokenProvider tokenProvider;
#Autowired
private UserDetailsServiceImpl userDetailsService;
#Override
protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
try {
String jwt = getJWTFromRequest(httpServletRequest);
if (StringUtils.hasText(jwt) && tokenProvider.validateToken(jwt)) {
String username = tokenProvider.getUsernameFromJwt(jwt);
User userDetails = (User) userDetailsService.loadUserByUsername(username);
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
userDetails, null, Collections.emptyList());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(httpServletRequest));
SecurityContextHolder.getContext().setAuthentication(authentication);
}
} catch (Exception ex) {
logger.error("Could not set user authentication in security context", ex);
}
filterChain.doFilter(httpServletRequest, httpServletResponse);
}
private String getJWTFromRequest(HttpServletRequest request) {
// Header Authorization: Bearer token
String bearerToken = request.getHeader("Authorization");
if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) {
return bearerToken.substring(7, bearerToken.length());
}
return null;
}
}
JwtTokenProvider.java
#Component
public class JwtTokenProvider {
// Generate the token
public String generateToken(Authentication authentication) {
User user = (User) authentication.getPrincipal();
Date now = new Date(System.currentTimeMillis());
Date expiryDate = new Date(now.getTime() + 300_000);
String userId = Long.toString(user.getId());
// this is what holds the token
// add roles in claims
Map<String, Object> claims = new HashMap<>();
claims.put("id", (Long.toString(user.getId())));
claims.put("username", user.getUsername());
return Jwts.builder().setSubject(userId).setClaims(claims).setIssuedAt(now).setExpiration(expiryDate)
.signWith(SignatureAlgorithm.HS512, "SECRETSECRETSECRET").compact();
}
// Validate the token
public boolean validateToken(String token) {
try {
Jwts.parser().setSigningKey("SECRETSECRETSECRET").parseClaimsJws(token);
return true;
} catch (SignatureException ex) {
System.out.println("Invalid JWT Signature");
} catch (MalformedJwtException ex) {
System.out.println("Invalid JWT Token");
} catch (ExpiredJwtException ex) {
System.out.println("Expired JWT Token");
} catch (UnsupportedJwtException ex) {
System.out.println("Unsupported JWT token");
} catch (IllegalArgumentException ex) {
System.out.println("JWT claims string is empty");
}
return false;
}
public String getUsernameFromJwt(String token) {
Claims claims = Jwts.parser().setSigningKey("SECRETSECRETSECRET").parseClaimsJws(token).getBody();
return claims.getSubject();
}
}
JwtAuthenticationEntryPoint.java
#Component
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint {
#Override
public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
InvalidLoginResponse loginResponse = new InvalidLoginResponse();
// InvalidLoginResponse is a class with username and password fields and a no-arg
constructor initialiting them like this
// this.username = "Invalid Username";
// this.password = "Invalid Password";
String jsonLoginResponse = new Gson().toJson(loginResponse);
httpServletResponse.setContentType("application/json");
httpServletResponse.setStatus(401);
httpServletResponse.getWriter().print("ERROR FROM JwtAuthenticationEntryPoint: " + jsonLoginResponse);
}
}
SecurityConfig.java
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true, jsr250Enabled = true, securedEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private JwtAuthenticationEntryPoint authenticationEntryPoint;
#Autowired
private UserDetailsServiceImpl userDetailsService;
#Bean
public JwtAuthenticationFilter jwtAuthenticationFilter() {
return new JwtAuthenticationFilter();
}
#Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Override
protected void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
authenticationManagerBuilder.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
// Here i am permitting access to all URLs but getting 401 when posting to /login
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint).and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and().authorizeRequests()
.anyRequest().permitAll();
http.addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
}
}
UserController.java
#RestController
#RequestMapping("/api/users")
#CrossOrigin
public class UserController {
private UserService userService;
private UserDetailsServiceImpl userDetailsService;
private UserValidator userValidator;
private ErrorValidationService errorValidationService;
private JwtTokenProvider tokenProvider;
private AuthenticationManager authenticationManager;
#Autowired
public UserController(UserService userService, UserDetailsServiceImpl userDetailsService, UserValidator userValidator, ErrorValidationService errorValidationService, JwtTokenProvider tokenProvider, AuthenticationManager authenticationManager) {
this.userService = userService;
this.userDetailsService = userDetailsService;
this.userValidator = userValidator;
this.errorValidationService = errorValidationService;
this.tokenProvider = tokenProvider;
this.authenticationManager = authenticationManager;
}
// I want to allow role based access to these URLs
#GetMapping("/all")
public String welcomeAll() {
return "Anyone can view this!";
}
#GetMapping("/admin")
public String adminPing(){
return "Only Admins Can view This";
}
#GetMapping("/user")
public String userPing(){
return "Any User Can view This";
}
#PostMapping("/register")
public ResponseEntity<?> register(#Valid #RequestBody User user, BindingResult result) {
userValidator.validate(user, result);
ResponseEntity<?> errorMap = errorValidationService.validationService(result);
if(errorMap != null) return errorMap;
User newUser = userService.saveUser(user);
return new ResponseEntity<User>(newUser, HttpStatus.CREATED);
}
#PostMapping("/login")
public ResponseEntity<?> generateToken(#Valid #RequestBody LoginRequest loginRequest, BindingResult result) throws Exception {
System.out.println("Entering /login");
ResponseEntity<?> errorMap = errorValidationService.validationService(result);
if(errorMap != null) return errorMap;
Authentication authentication = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(loginRequest.getUsername(), loginRequest.getPassword()));
SecurityContextHolder.getContext().setAuthentication(authentication);
String jwt = "Bearer " + tokenProvider.generateToken(authentication);
return ResponseEntity.ok(new JwtLoginSuccessResponse(true, jwt));
}
}
When trying to register in postman
/api/users/register
When trying to login in postman
/api/users/register
As you added OncePerRequestFilter for all the incoming request. So when you call /api/users/login it will check for JWT token and unable to find it in header so it is throwing 401 (Unauthorized). To exclude /api/users/login end point from OncePerRequestFilter you need to override shouldNotFilter(HttpServletRequest request)
public class LoginFilter extends OncePerRequestFilter {
private List<String> excludeUrlPatterns = new ArrayList<String>();
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException
{
//Business logic
}
#Override
protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException
{
// add excludeUrlPatterns on which one to exclude here
}
}
For more info visit :OncePerRequestFilter
Related
I am able to register a user then login with those credentials and receive a valid JWT token with userId, username and the roles of a user.
Here is a screenshot of a token with 2 roles (USER and ADMIN)
jwt.io result
I inserted 2 roles (USER and ADMIN) into my roles table manually so that each user created will automatically be assigned a ROLE_USER once created.
The problem is, now that I want to access URLs based on the roles a user has, I am getting 401 unauthorized on endpoints that I tried to grant access to.
This is a postman response from a User who has a role of User and Admin, but I'm getting this 401 still.
postman response
Here are the important classes
My Role entity has a name and description fields
My Repository interfaces have a findByXXX method
User.java
#Entity
#Table(name="user")
public class User implements UserDetails{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(unique = true)
#NotBlank(message = "Username is required")
private String username;
#NotBlank(message = "Password is required")
private String password;
#Transient
private String confirmPassword;
private boolean enabled = true;
#JsonFormat(pattern = "yyyy-mm-dd")
private Date createdAt;
// I want to load all the roles of users + the user itself once requested for
#ManyToMany(fetch=FetchType.EAGER, cascade = CascadeType.ALL)
#JoinTable(name="user_roles",
joinColumns = {
#JoinColumn(name="user_id")
},
inverseJoinColumns = {
#JoinColumn(name="role_id")
})
private Set<Role> roles = new HashSet<>();
// Constructor
// Getters and setteres
#Override
#JsonIgnore
public Collection<? extends GrantedAuthority> getAuthorities() {
return null;
}
#Override
#JsonIgnore
public boolean isAccountNonExpired() {
return true;
}
#Override
#JsonIgnore
public boolean isAccountNonLocked() {
return true;
}
#Override
#JsonIgnore
public boolean isCredentialsNonExpired() {
return true;
}
}
SecurityConfig.java
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true, jsr250Enabled = true, securedEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private JwtAuthenticationEntryPoint authenticationEntryPoint;
#Autowired
private UserDetailsServiceImpl userDetailsService;
#Bean
public JwtAuthenticationFilter jwtAuthenticationFilter() {
return new JwtAuthenticationFilter();
};
#Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/api/users/register", "/api/users/login", "/api/users/all").permitAll()
.antMatchers("/api/users/user/**").hasRole("USER")
.antMatchers("/api/users/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
.and()
// handles the json exception response if details are incorrect with an appropriate message
.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint);
httpSecurity.addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
}
}
UserService.java
#Service
public class UserService {
#Autowired
private UserRepository userRepository;
#Autowired
private BCryptPasswordEncoder passwordEncoder;
#Autowired
private RoleService roleService;
public User saveUser(User user) {
Role userRole = roleService.findByRoleName("USER");
// Role adminRole = roleService.findByRoleName("ADMIN");
Set<Role> roles = new HashSet<>();
try {
user.setPassword(passwordEncoder.encode(user.getPassword()));
user.setUsername(user.getUsername());
user.setConfirmPassword("");
roles.add(userRole);
user.setRoles(roles);
return userRepository.save(user);
} catch(Exception e) {
throw new UsernameAlreadyExistsException("User with username " + user.getUsername() + " already exists!");
}
}
public List<User> findAll() {
List<User> list = new ArrayList<>();
userRepository.findAll().iterator().forEachRemaining(list::add);
return list;
}
}
UserDetailsServiceImpl.java
#Service
public class UserDetailsServiceImpl implements UserDetailsService {
#Autowired
private UserRepository userRepository;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userRepository.findByUsername(username);
if(user == null) {
throw new UsernameNotFoundException("User not found");
}
return user;
}
private Set getAuthority(User user) {
Set<SimpleGrantedAuthority> authorities = new HashSet<>();
user.getRoles().forEach(role -> {
authorities.add(new SimpleGrantedAuthority("ROLE_" + role.getName()));
});
return authorities;
}
}
JwtTokenProvider.java
#Component
public class JwtTokenProvider {
private final String SECRET = "SECRET";
// Generate the token
public String generateToken(Authentication authentication) {
User user = (User) authentication.getPrincipal();
Date now = new Date(System.currentTimeMillis());
Date expiryDate = new Date(now.getTime() + 300_000);
String userId = Long.toString(user.getId());
// this is what holds the token
// add roles in claims
Map<String, Object> claims = new HashMap<>();
claims.put("id", (Long.toString(user.getId())));
claims.put("username", user.getUsername());
claims.put("roles", user.getRoles());
return Jwts.builder().setSubject(userId).setClaims(claims).setIssuedAt(now).setExpiration(expiryDate)
.signWith(SignatureAlgorithm.HS512, SECRET).compact();
}
// Validate the token
public boolean validateToken(String token) {
try {
Jwts.parser().setSigningKey(SECRET).parseClaimsJws(token);
return true;
} catch (SignatureException ex) {
System.out.println("Invalid JWT Signature");
} catch (MalformedJwtException ex) {
System.out.println("Invalid JWT Token");
} catch (ExpiredJwtException ex) {
System.out.println("Expired JWT Token");
} catch (UnsupportedJwtException ex) {
System.out.println("Unsupported JWT token");
} catch (IllegalArgumentException ex) {
System.out.println("JWT claims string is empty");
}
return false;
}
public String getUsernameFromJWT(String token) {
Claims claims = Jwts.parser().setSigningKey(SECRET).parseClaimsJws(token).getBody();
return claims.getSubject();
}
}
JwtAuthenticationFilter.java
public class JwtAuthenticationFilter extends OncePerRequestFilter {
#Autowired
private JwtTokenProvider tokenProvider;
#Autowired
private UserDetailsServiceImpl userDetailsService;
#Override
protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse,
FilterChain filterChain) throws ServletException, IOException {
try {
String jwt = getJWTFromRequest(httpServletRequest);
if (StringUtils.hasText(jwt) && tokenProvider.validateToken(jwt)) {
String username = tokenProvider.getUsernameFromJWT(jwt);
User userDetails = (User) userDetailsService.loadUserByUsername(username);
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
userDetails, null, Collections.emptyList());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(httpServletRequest));
SecurityContextHolder.getContext().setAuthentication(authentication);
}
} catch (Exception ex) {
logger.error("Could not set user authentication in security context", ex);
}
filterChain.doFilter(httpServletRequest, httpServletResponse);
}
private String getJWTFromRequest(HttpServletRequest request) {
// Header Authorization: Bearer token
String bearerToken = request.getHeader("Authorization");
if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) {
return bearerToken.substring(7, bearerToken.length());
}
return null;
}
}
UserController.java
#RestController
#RequestMapping("/api/users")
#CrossOrigin
public class UserController {
private UserService userService;
private UserDetailsServiceImpl userDetailsService;
private UserValidator userValidator;
private ErrorValidationService errorValidationService;
private JwtTokenProvider tokenProvider;
private AuthenticationManager authenticationManager;
#Autowired
public UserController(UserService userService, UserDetailsServiceImpl userDetailsService, UserValidator userValidator, ErrorValidationService errorValidationService, JwtTokenProvider tokenProvider, AuthenticationManager authenticationManager) {
this.userService = userService;
this.userDetailsService = userDetailsService;
this.userValidator = userValidator;
this.errorValidationService = errorValidationService;
this.tokenProvider = tokenProvider;
this.authenticationManager = authenticationManager;
}
// I want to allow role based access to these URLs
#GetMapping("/all")
public String welcomeAll() {
return "Anyone can view this!";
}
#GetMapping("/admin")
public String adminPing(){
return "Only Admins Can view This";
}
#GetMapping("/user")
public String userPing(){
return "Any User Can view This";
}
// Get all users
#GetMapping("/userList")
public ResponseEntity<?> getAllUsers() {
return ResponseEntity.ok(userService.findAll());
}
#PostMapping("/register")
public ResponseEntity<?> register(#Valid #RequestBody User user, BindingResult result) {
userValidator.validate(user, result);
ResponseEntity<?> errorMap = errorValidationService.validationService(result);
if(errorMap != null) return errorMap;
User newUser = userService.saveUser(user);
return new ResponseEntity<User>(newUser, HttpStatus.CREATED);
}
#PostMapping("/login")
public ResponseEntity<?> login(#Valid #RequestBody LoginRequest loginRequest, BindingResult result) throws Exception {
System.out.println("Entering /login");
ResponseEntity<?> errorMap = errorValidationService.validationService(result);
if(errorMap != null) return errorMap;
Authentication authentication = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(loginRequest.getUsername(), loginRequest.getPassword()));
SecurityContextHolder.getContext().setAuthentication(authentication);
String jwt = "Bearer " + tokenProvider.generateToken(authentication);
return ResponseEntity.ok(new JwtLoginSuccessResponse(true, jwt));
}
}
If anyone can let me know why this is happening , and how I can go about changing and abstracting things in a much better way I would appreciate it a lot. Since there's so many different implementations online I'm not sure what i'm doing wrong.
Using hasAnyAuthority instead of hasRole and remove "ROLE_" from SimpleGrantedAuthority
Your code
.antMatchers("/api/users/user/**").hasRole("USER")
//...and...
authorities.add(new SimpleGrantedAuthority("ROLE_" + role.getName()));
Try this
.antMatchers("/api/users/user/**").hasAnyAuthority("USER")
//...and...
authorities.add(new SimpleGrantedAuthority(role.getName()));
But I dont't see where you use method UserDetailsServiceImpl.getAuthority? You put an empty list of authorities into UsernamePasswordAuthenticationToken in JwtAuthenticationFilter
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
userDetails, null, Collections.emptyList());
You should put authorities in.
So, I have two different types of users, a User entity that has a role of USER and a HOST entity that has a role of HOST, and roles have permissions accordingly.
I created a UserPrincipal that implements UserDetails where i override the getAuthorities() method and add a simpleGrantedAuthority.
I created roles and permissions as Enums.
This is the ApplicationUserPermission Enum that specifies the permissions.
public enum ApplicationUserPermission {
USER_READ("user:read"),
USER_WRITE("user:write"),
HOST_READ("host:read"),
HOST_WRITE("host:write"),
APARTMENT_READ("apartment:read"),
APARTMENT_WRITE("apartment:write");
private final String permission;
ApplicationUserPermission(String permission) {
this.permission = permission;
}
public String getPermission() {
return permission;
}
and then i have the ApplicationUserRole that sets these permissions to two different roles: USER and HOST.
public enum ApplicationUserRole {
USER(Sets.newHashSet(USER_READ, USER_WRITE, APARTMENT_READ)),
HOST(Sets.newHashSet(HOST_READ, HOST_WRITE, APARTMENT_READ, APARTMENT_WRITE));
private final Set<ApplicationUserPermission> permissions;
ApplicationUserRole(Set<ApplicationUserPermission> permissions) {
this.permissions = permissions;
}
public Set<ApplicationUserPermission> getPermissions() {
return permissions;
}
Then I have the UserPrincipal that implements UserDetails and sets the authorities for User entity
public class UserPrincipal implements UserDetails {
private final User user;
public UserPrincipal(User user) {
super();
this.user = user;
}
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return Collections.singleton(new SimpleGrantedAuthority("ROLE_"+USER.name()));
}
Then I have my custom UserDetailsService that implements UserDetailsService
#Service
public class MyUserDetailsService implements UserDetailsService {
#Autowired
private UserRepository userRepository;
#Autowired
private HostRepository hostRepository;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
String lastDigits = username.substring(username.length() - 3);
User user;
Host host;
if (username.contains("#")) {
host = hostRepository.findByEmail(username);
if (host == null) {
throw new UsernameNotFoundException("Server 404");
}
return new HostPrincipal(host);
}
user = userRepository.findByUsername(username);
if (user == null) {
throw new UsernameNotFoundException("Server 404");
}
return new UserPrincipal(user);
}
This is my ApplicationSecurityConfig.java:
#Configuration
#EnableWebSecurity
public class AppSecurityConfig extends WebSecurityConfigurerAdapter {
#Qualifier("myUserDetailsService")
#Autowired
private UserDetailsService userDetailsService;
#Autowired
private AuthenticationEntryPoint authenticationEntryPoint;
#Bean
public UserDetailsService userDetailsService() {
return super.userDetailsService();
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder(10);
}
#Bean
public AuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
provider.setUserDetailsService(userDetailsService);
provider.setPasswordEncoder(passwordEncoder());
return provider;
}
#Override
protected void configure(AuthenticationManagerBuilder auth) {
auth.authenticationProvider(authenticationProvider());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.cors().disable()
.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and() // check if user exist in database and create a token for specific user
.addFilter(new JwtUsernameAndPasswordAuthAuthenticationFilter(authenticationManager()))
.addFilterAfter(new JwtTokenVerifier(), JwtUsernameAndPasswordAuthAuthenticationFilter.class)
.authorizeRequests()
.antMatchers("/", "/login", "/createHost", "/createUser", "/getAllApartments").permitAll()
.antMatchers("/users/**").hasRole("USER")
.antMatchers("/hosts/**").hasRole(HOST.name()).anyRequest().authenticated()
.httpBasic().authenticationEntryPoint(authenticationEntryPoint);
}
This is my JwtUsernameAndPasswordAuthFilter.java:
public class JwtUsernameAndPasswordAuthAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
private static final Logger logger = LoggerFactory.
getLogger(JwtUsernameAndPasswordAuthAuthenticationFilter.class);
private final AuthenticationManager authenticationManager;
public JwtUsernameAndPasswordAuthAuthenticationFilter(AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
}
#Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
try {
UsernamePasswordAuthenticationRequest authenticationRequest =
new ObjectMapper().readValue(request.getInputStream(),
UsernamePasswordAuthenticationRequest.class);
Authentication authentication = new UsernamePasswordAuthenticationToken(
authenticationRequest.getUsername(),
authenticationRequest.getPassword()
);
authenticationManager.authenticate(authentication);
return authentication;
} catch (IOException e) {
throw new RuntimeException(e);
}
}
#Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
logger.info("Auth result: {}", authResult);
logger.info("Authorities: {}", authResult.getAuthorities());
String key = "securesecuresecuresecuresecuresecuresecuresecuresecuresecuresecure";
String token = Jwts.builder()
.setSubject(authResult.getName())
.claim("authorities", authResult.getAuthorities())
.setIssuedAt(Date.valueOf(LocalDate.now().plusWeeks(2)))
.signWith(Keys.hmacShaKeyFor(key.getBytes()))
.compact();
String tokenAsJson = new JSONObject()
.put("token", token)
.toString();
response.addHeader("Authorization", "Bearer " + token);
response.setContentType("application/json");
response.getWriter().write(tokenAsJson);
}
EDIT: This is my Authorization filter
public class JwtTokenVerifier extends BasicAuthenticationFilter {
public JwtTokenVerifier(AuthenticationManager authenticationManager){
super(authenticationManager);
}
#Override
protected void doFilterInternal(
HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
String authorizationHeader = request.getHeader("Authorization");
if (Strings.isNullOrEmpty(authorizationHeader) || !authorizationHeader.startsWith("Bearer ")) {
filterChain.doFilter(request, response);
return;
}
String secretKey = "securesecuresecuresecuresecuresecuresecuresecuresecuresecuresecure";
String token = authorizationHeader.replace("Bearer ", "");
try {
Jws<Claims> claimsJws = Jwts.parser().
setSigningKey(Keys.hmacShaKeyFor(secretKey.getBytes())).parseClaimsJws(token);
Claims body = claimsJws.getBody();
String username = body.getSubject();
var authorities = (List<Map<String, String>>) body.get("authorities");
Set<SimpleGrantedAuthority> simpleGrantedAuthoritySet = authorities.stream()
.map(m -> new SimpleGrantedAuthority(m.get("authority"))).collect(Collectors.toSet());
Authentication authentication = new UsernamePasswordAuthenticationToken(username,
null, simpleGrantedAuthoritySet);
SecurityContextHolder.getContext().setAuthentication(authentication);
} catch (JwtException e) {
throw new IllegalStateException(String.format("Token %s cannot be trusted", token));
}
filterChain.doFilter(request, response);
}
}
authResult.getAuthorities() returns empty [].
If I try to access "/users/**" after I logged in as a User it returns 403 Forbidden.
I am trying to test my Spring Application which consists of JWT auth with Junit and Mockmvc. Signup test is working as expected. But when trying to test login, StackOverflowError is displayed. Inside the JWTAuthenticationFilter.java, data is being successfully received. But after that error is displayed. Please help me out. Thanks!
Error:
java.lang.StackOverflowError
at org.mockito.internal.invocation.MatchersBinder.bindMatchers(MatchersBinder.java:25)
at org.mockito.internal.handler.MockHandlerImpl.handle(MockHandlerImpl.java:59)
at org.mockito.internal.handler.NullResultGuardian.handle(NullResultGuardian.java:29)
at org.mockito.internal.handler.InvocationNotifierHandler.handle(InvocationNotifierHandler.java:35)
at org.mockito.internal.creation.bytebuddy.MockMethodInterceptor.doIntercept(MockMethodInterceptor.java:63)
at org.mockito.internal.creation.bytebuddy.MockMethodInterceptor.doIntercept(MockMethodInterceptor.java:49)
at org.mockito.internal.creation.bytebuddy.MockMethodInterceptor$DispatcherDefaultingToRealMethod.interceptSuperCallable(MockMethodInterceptor.java:110)
at org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder$MockitoMock$310380589.encode(Unknown Source)
at org.springframework.security.authentication.dao.DaoAuthenticationProvider.prepareTimingAttackProtection(DaoAuthenticationProvider.java:142)
at org.springframework.security.authentication.dao.DaoAuthenticationProvider.retrieveUser(DaoAuthenticationProvider.java:106)
at org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider.authenticate(AbstractUserDetailsAuthenticationProvider.java:144)
at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:175)
at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:200)
at org.springframework.security.con
WebSecurity.java
#EnableWebSecurity
public class WebSecurity extends WebSecurityConfigurerAdapter {
private UserDetailsServiceImpl userDetailsService;
private BCryptPasswordEncoder bCryptPasswordEncoder;
public WebSecurity(UserDetailsServiceImpl userDetailsService, BCryptPasswordEncoder bCryptPasswordEncoder) {
this.userDetailsService = userDetailsService;
this.bCryptPasswordEncoder = bCryptPasswordEncoder;
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable().authorizeRequests()
.antMatchers(HttpMethod.POST, SecurityConstants.SIGN_UP_URL).permitAll()
.anyRequest().authenticated()
.and()
.addFilter(new JWTAuthenticationFilter(authenticationManager()))
.addFilter(new JWTAuthorizationFilter(authenticationManager()))
// this disables session creation on Spring Security
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception{
return super.authenticationManagerBean();
}
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.parentAuthenticationManager(authenticationManagerBean())
.userDetailsService(userDetailsService)
.passwordEncoder(bCryptPasswordEncoder);
}
}
UserControllerTest.java
#RunWith(SpringRunner.class)
#SpringBootTest
#AutoConfigureMockMvc
#AutoConfigureJsonTesters
public class UserControllerTest {
#Autowired
private MockMvc mvc;
#Autowired
private JacksonTester<User> json;
#Autowired
private JacksonTester<CreateUserRequest> jsonN;
#MockBean
private UserRepository userRepository;
#MockBean
private BCryptPasswordEncoder encoder;
private CreateUserRequest r;
#Before
public void setup(){
r = new CreateUserRequest();
r.setUsername("ujjwal2102");
r.setPassword("ujjwal21");
r.setConfirmPassword("ujjwal21");
}
#Test
public void createUserTest() throws Exception{
signup();
}
#Test
public void loginUserTest() throws Exception{
signup();
login();
}
public void signup() throws Exception{
when(encoder.encode("ujjwal21")).thenReturn("ujjwal21");
mvc.perform(
post(new URI("/api/user/create"))
.content(jsonN.write(r).getJson())
.contentType(MediaType.APPLICATION_JSON_UTF8)
.accept(MediaType.APPLICATION_JSON_UTF8))
.andExpect(status().isOk())
.andExpect(jsonPath("$.id",is(0)))
.andExpect(jsonPath("$.username",is("ujjwal2102")));
}
public void login() throws Exception{
User user = new User();
user.setUsername("ujjwal2102");
user.setPassword("ujjwal21");
when(encoder.encode("ujjwal21")).thenReturn("ujjwal21");
mvc.perform(
post(new URI("/login"))
.content(json.write(user).getJson())
.contentType(MediaType.APPLICATION_JSON_UTF8)
.accept(MediaType.APPLICATION_JSON_UTF8))
.andExpect(status().isOk());
}
}
User.java
#Entity
#Table(name = "user")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#JsonProperty
private long id;
#Column(nullable = false, unique = true)
#JsonProperty
private String username;
#JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
#Column(nullable = false)
private String password;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "cart_id", referencedColumnName = "id")
#JsonIgnore
private Cart cart;
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Cart getCart() {
return cart;
}
public void setCart(Cart cart) {
this.cart = cart;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
}
JWTAuthenticationFilter.java
public class JWTAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
private AuthenticationManager authenticationManager;
public JWTAuthenticationFilter(AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
}
#Override
public Authentication attemptAuthentication(HttpServletRequest req,
HttpServletResponse res) throws AuthenticationException {
try {
User creds = new ObjectMapper()
.readValue(req.getInputStream(), User.class);
System.out.println("USERNAME-----" + creds.getUsername());
return authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(
creds.getUsername(),
creds.getPassword(),
new ArrayList<>())
);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
#Override
protected void successfulAuthentication(HttpServletRequest req,
HttpServletResponse res,
FilterChain chain,
Authentication auth) throws IOException, ServletException {
String token = JWT.create()
.withSubject(((org.springframework.security.core.userdetails.User) auth.getPrincipal()).getUsername())
.withExpiresAt(new Date(System.currentTimeMillis() + SecurityConstants.EXPIRATION_TIME))
.sign(HMAC512(SecurityConstants.SECRET.getBytes()));
System.out.println("TOKEN----" + token);
res.addHeader(SecurityConstants.HEADER_STRING, SecurityConstants.TOKEN_PREFIX + token);
}
}
I noticed one mistake that may or may not solved your issue, but may compromise your authentification process.
new UsernamePasswordAuthenticationToken(
creds.getUsername(),
creds.getPassword(),
new ArrayList<>()) // with autorities, the user is authenticated
new UsernamePasswordAuthenticationToken(
creds.getUsername(),
creds.getPassword()) // without autorities, the user is not authenticated
The constructor with autorities is usually used by the AuthentificationManager after authentification is successful. The constructor without is used by the filter to pass to the AuthentificationManager.
Spring Security Javadoc
Looking at the log, the issue looks to be in your Spring Security Configuration that you didn't provide. You may have done a recursive call inside an authentification provider.
The successfulAuthentication function in JWTAuthenticationFilter.java gives a nullpointerexception. Do you see why this would be an issue? Is it a problem with using the same bean for autowiring?
This is my current project structure:
-com
-register
-RegisterController.java
-security
-JWTAuthenticationFilter.java
-JWTAuthorizationFilter.java
-SecurityConstants.java
-WebSecurity.java
-user
-User.java
-UserRepository.java
-UserService.java
-Application.java
Application.java
#Configuration
#SpringBootApplication
public class Application {
#Bean
public BCryptPasswordEncoder bCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
}
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
UserRepository.java
#Repository
public interface UserRepository extends MongoRepository<User, String> {
User findByUsername(String name);
User findByEmail(String Email);
User findBy_id(ObjectId id);
}
UserService.java
#Service
public class UserService implements UserDetailsService {
#Autowired
private UserRepository userRepository;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = this.userRepository.findByUsername(username);
if(user == null)
return null;
List<SimpleGrantedAuthority> authorities = Arrays.asList(new SimpleGrantedAuthority("user"));
return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(), authorities);
}
public User getUserByUsername(String username) {
return this.userRepository.findByUsername(username);
}
public User getUserBy_id(ObjectId _id) {
return userRepository.findBy_id(_id);
}
public void saveUser(User newUser){
userRepository.save(newUser);
}
}
User.java
#Document
public final class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private ObjectId _id;
private String email;
private String username;
private String password;
private AccountProperties accountProperties;
private Address address;
private List<Pet> pets = new ArrayList<>();
private String phoneNumber;
public User() {}
public User(#JsonProperty("email") String email, #JsonProperty("username") String username,
#JsonProperty("password") String password) {
this.email = email;
this.username = username;
this.password = password;
}
public String get_id() { return _id.toHexString();}
getters and setters() ...
}
JWTAuthenticationFilter.java
public class JWTAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
#Autowired
private UserRepository userRepo;
private AuthenticationManager authenticationManager;
JWTAuthenticationFilter(AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
}
#Override
public Authentication attemptAuthentication(HttpServletRequest req,
HttpServletResponse res) throws AuthenticationException {
try {
User creds = new ObjectMapper()
.readValue(req.getInputStream(), User.class);
return authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(
creds.getUsername(),
creds.getPassword(),
new ArrayList<>())
);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
#Override
protected void successfulAuthentication(HttpServletRequest req,
HttpServletResponse res,
FilterChain chain,
Authentication auth) throws IOException, ServletException {
String username = ((org.springframework.security.core.userdetails.User) auth.getPrincipal()).getUsername();
String token = JWT.create()
.withSubject(username)
.withExpiresAt(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
.sign(HMAC512(SECRET.getBytes()));
res.addHeader(HEADER_STRING, TOKEN_PREFIX + token);
User u = uRepo.findByUsername("admin");
res.getWriter().write(
"{\"" + SecurityConstants.HEADER_STRING + "\":\"" + SecurityConstants.TOKEN_PREFIX+token + "\"," +
"\"" + "ObjectID" + "\":\"" + u.get_id() + "\"}"
);
}
}
JWTAuthorizationFilter.java
public class JWTAuthorizationFilter extends BasicAuthenticationFilter {
public JWTAuthorizationFilter(AuthenticationManager authManager) {
super(authManager);
}
#Override
protected void doFilterInternal(HttpServletRequest req,
HttpServletResponse res,
FilterChain chain) throws IOException, ServletException {
String header = req.getHeader(HEADER_STRING);
if (header == null || !header.startsWith(TOKEN_PREFIX)) {
chain.doFilter(req, res);
return;
}
UsernamePasswordAuthenticationToken authentication = getAuthentication(req);
SecurityContextHolder.getContext().setAuthentication(authentication);
chain.doFilter(req, res);
}
private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest request) {
String token = request.getHeader(HEADER_STRING);
if (token != null) {
// parse the token.
String user = JWT.require(Algorithm.HMAC512(SECRET.getBytes()))
.build()
.verify(token.replace(TOKEN_PREFIX, ""))
.getSubject();
if (user != null) {
return new UsernamePasswordAuthenticationToken(user, null, new ArrayList<>());
}
return null;
}
return null;
}
}
WebSecurity.java
#EnableWebSecurity
public class WebSecurity extends WebSecurityConfigurerAdapter {
#Autowired
private UserService userDetailsService;
#Autowired
private BCryptPasswordEncoder bCryptPasswordEncoder;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable().authorizeRequests()
.antMatchers(HttpMethod.POST, SIGN_UP_URL).permitAll()
.anyRequest().authenticated()
.and()
.addFilter(new JWTAuthenticationFilter(authenticationManager()))
.addFilter(new JWTAuthorizationFilter(authenticationManager()))
// this disables session creation on Spring Security
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS );
}
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {auth.userDetailsService(this.userDetailsService).passwordEncoder(this.bCryptPasswordEncoder);}
#Bean
CorsConfigurationSource corsConfigurationSource() {
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", new CorsConfiguration().applyPermitDefaultValues());
return source;
}
}
SecurityConstants.java
public class SecurityConstants {
public static final String SECRET = "SecretKeyToGenJWTs";
public static final long EXPIRATION_TIME = 864_000_000; // 10 days
public static final String TOKEN_PREFIX = "Bearer ";
public static final String HEADER_STRING = "Authorization";
public static final String SIGN_UP_URL = "/users/sign-up";
}
RegisterController.java
#RestController
#RequestMapping("/users")
public class RegisterController {
#Autowired
private UserService userService;
#Autowired
private BCryptPasswordEncoder bCryptPasswordEncoder;
#PostMapping("/sign-up")
public void signUp(#RequestBody User user) {
if (user.getPassword() == null || user.getUsername() == null)
return;
user.setPassword(bCryptPasswordEncoder.encode(user.getPassword()));
userService.saveUser(user);
}
}
Not sure if this is the root cause of your issue, but I have never seen #Configuration in the main application. I would try to move that to a separate config class and see if that helps
Annotate the JWTAuthenticationFilter with #Component or add #Bean in configuration file . Looks like the object is not created
The problem is that you doesn't define JWTAuthenticationFilter as Bean, so spring doesn't inject dependencies in it.
You can get beans in filter manualy. From the GenericFilterBean javadoc:
This generic filter base class has no dependency on the Spring org.springframework.context.ApplicationContext concept. Filters usually don't load their own context but rather access service beans from the Spring root application context, accessible via the filter's ServletContext (see org.springframework.web.context.support.WebApplicationContextUtils).
Or you can make it bean. But if you are using Spring Boot consider that:
The fact that all filters internal to Spring Security are unknown to the container is important, especially in a Spring Boot application, where all #Beans of type Filter are registered automatically with the container by default. So if you want to add a custom filter to the security chain, you need to either not make it a #Bean or wrap it in a FilterRegistrationBean that explicitly disables the container registration.
As the title says, I'm using Spring Security with JWT (using hibernate and bCrypt) to sign up users and let them login. I've followed this tutorial to make this work in my project. When doing exactly the same as the tutorial (using the in-memory-database) everythings seems to work fine. But when intergrating the code in my own project the authentication keeps failing, giving a "bad credentials" exception.
Main:
#SpringBootApplication
#EnableConfigurationProperties
public class ApiApplication {
#Bean
public BCryptPasswordEncoder bCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
}
public static void main(String[] args) {
SpringApplication.run(ApiApplication.class, args);
}
}
My websecurity config looks like this:
#EnableWebSecurity
public class WebSecurity extends WebSecurityConfigurerAdapter {
private UserDetailsService userDetailsService;
private BCryptPasswordEncoder bCryptPasswordEncoder;
public WebSecurity(UserDetailsService userDetailsService,
BCryptPasswordEncoder bCryptPasswordEncoder) {
this.userDetailsService = userDetailsService;
this.bCryptPasswordEncoder = bCryptPasswordEncoder;
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable().authorizeRequests()
.antMatchers(HttpMethod.POST, SIGN_UP_URL).permitAll()
.anyRequest().authenticated()
.and()
.addFilter(new JWTAuthenticationFilter(authenticationManager()))
.addFilter(new JWTAuthorizationFilter(authenticationManager()))
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder);
}
#Bean
CorsConfigurationSource corsConfigurationSource() {
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", new CorsConfiguration().applyPermitDefaultValues());
return source;
}
}
The authentication filter, whenever I try to send a post with the username and password, the unsuccessfulAuthentication runs, even with correct credentials.
public class JWTAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
private AuthenticationManager authenticationManager;
public JWTAuthenticationFilter(AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
}
#Override
public Authentication attemptAuthentication(HttpServletRequest req,
HttpServletResponse res) throws AuthenticationException {
try {
User creds = new ObjectMapper()
.readValue(req.getInputStream(), User.class);
return authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(
creds.getUsername(),
creds.getUsername(),
new ArrayList<>())
);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
#Override
protected void successfulAuthentication(HttpServletRequest req,
HttpServletResponse res,
FilterChain chain,
Authentication auth) throws IOException, ServletException {
System.out.println("This method never runs...");
Claims claims = Jwts.claims()
.setSubject(((org.springframework.security.core.userdetails.User) auth.getPrincipal()).getUsername())
.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME));
String token = Jwts.builder()
.setClaims(claims)
.signWith(SignatureAlgorithm.HS512, SECRET.getBytes())
.compact();
res.addHeader(HEADER_STRING, TOKEN_PREFIX + token);
}
#Override
protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException {
super.unsuccessfulAuthentication(request, response, failed);
System.out.println("FAILED");
failed.printStackTrace(); // bad creds
}
}
UserDetailServiceImpl:
#Service
public class UserDetailsServiceImpl implements UserDetailsService {
private UserRepository repository;
public UserDetailsServiceImpl(UserRepository applicationUserRepository) {
this.repository = applicationUserRepository;
}
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = repository.findByUsername(username);
if(user == null){
System.out.println("User is null");
throw new UsernameNotFoundException(username);
}
return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(), emptyList());
}
}
The user entity:
#Data
#Entity
#Table(name = "user_entity")
public class User implements Serializable {
public User() { }
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
#Column(name = "user_id", unique = true)
private long userId;
#NotEmpty
#Column(name = "username", unique = true)
private String username;
#NotEmpty
#Column(name = "user_rol")
#JsonProperty("userRol")
private String userRol;
#NotEmpty
#Column(name = "password")
private String password;
}
In my user controller I encrypt the password like this:
#PostMapping("/sign-up")
public User signUp(#RequestBody User user) {
user.setPassword(passwordEncoder.encode(user.getPassword()));
user.setUserRol("ADMIN");
return userService.signUpUser(user);
}
Everythings seems to be ok, Spring succesfully puts a new user in the database (with an encrypted password), whenever I do a login attempt it also succesfully gets that user, but authentication still fails (with correct credentials ofcourse). So my guess there is something wrong with the bcypt password encoder...
Another thing I wonder; where does the /login route come from? Is this a default route within Spring Security? (I've never declared it)
Thanks for the help guys!
Maybe it is due a typo in your attemptAuthentication() method.
#Override
public Authentication attemptAuthentication(HttpServletRequest req,
HttpServletResponse res) throws AuthenticationException {
try {
User creds = new ObjectMapper()
.readValue(req.getInputStream(), User.class);
return authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(
creds.getUsername(),
creds.getUsername(),
new ArrayList<>())
);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
In the constructor of UsernamePasswordAuthenticationToken the second argument should be creds.getPassword() and not creds.getUsername().