I use SpringSecurity in my web-project. When I try to login with username and password, I have this error in my stacktrace :
class org.springframework.security.core.userdetails.User cannot be cast to class com.cesi.cuberil.service.JwtUser (org.springframework.security.core.userdetails.User and com.cesi.cuberil.service.JwtUser are in unnamed module of loader 'app') at com.cesi.cuberil.controller.AuthController.login(AuthController.java:116) ~[classes/:na] at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na] at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na] at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na] at java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na] at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:197) ~[spring-web-5.3.2.jar:5.3.2]
Below, I will put classes where I think the problem comes from. Here my JwtUser.java :
package com.cesi.cuberil.service; import com.cesi.cuberil.model.User; import lombok.Data; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; import java.util.Collection; @Data public class JwtUser implements UserDetails { private final String username; private final String password; private final boolean enabled; private final Collection<? extends GrantedAuthority> authorities; private static final long serialVersionUID = 1L; private User user; public JwtUser(String username, String password, Boolean enabled, Collection<? extends GrantedAuthority> authorities) { this.username = username; this.password = password; this.enabled = enabled; this.authorities = authorities; } public User getUser() { return user; } public void setUser(User user) { this.user = user; } @Override public Collection<? extends GrantedAuthority> getAuthorities() { return authorities; } @Override public String getPassword() { return password; } @Override public String getUsername() { return username; } @Override public boolean isAccountNonExpired() { return true; } @Override public boolean isAccountNonLocked() { return true; } @Override public boolean isCredentialsNonExpired() { return true; } @Override public boolean isEnabled() { return enabled; } }
Here my AuthController.java :
@RestController @RequestMapping("/api/v1/auth") @AllArgsConstructor public class AuthController { @Autowired AuthenticationManager authenticationManager; @Autowired PasswordEncoder encoder; @Autowired JwtUtils jwtUtils; @PostMapping("/login") public ResponseEntity<?> login(@Valid @RequestBody LoginRequest loginRequest) { Authentication authentication = authenticationManager.authenticate( new UsernamePasswordAuthenticationToken(loginRequest.getUsername(), loginRequest.getPassword())); SecurityContextHolder.getContext().setAuthentication(authentication); String jwt = jwtUtils.generateJwtToken(authentication); JwtUser myUserDetails = (JwtUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); List<String> roles = myUserDetails.getAuthorities().stream() .map(item -> item.getAuthority()) .collect(Collectors.toList()); return ResponseEntity.ok(new JwtResponse(jwt, myUserDetails.getUser().getUserId(), myUserDetails.getUsername(), myUserDetails.getUser().getEmail(), roles)); } }
Here my UserDetailsServiceImpl.java :
@Service @AllArgsConstructor public class UserDetailsServiceImpl implements UserDetailsService { @Autowired private UserRepository userRepository; public User getByUsername(String username) { return userRepository.findByUsername(username); } @Override @Transactional(readOnly = true) public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { User user = this.getByUsername(username); if (user == null) { throw new UsernameNotFoundException("user not found"); } else { List<GrantedAuthority> listAuthorities = new ArrayList<GrantedAuthority>(); listAuthorities.add(new SimpleGrantedAuthority("ROLE_USER")); return new org.springframework.security.core.userdetails.User(username, user.getPassword(), true, true, true, true, listAuthorities); } } }
For information, I use java 11 and the lastest version of Spring Boot.
Advertisement
Answer
I think you should return JwtUser
instance instead of org.springframework.security.core.userdetails.User
. Cause JwtUser
already implements UserDetails
interface.
Here’s an sample.