'class java.lang.String cannot be cast to class org.springframework.security.core.userdetails.UserDetails
I'm making a reddit clone for practice and when I run it I'm getting the following exception:
java.lang.ClassCastException: class java.lang.String cannot be cast to class org.springframework.security.core.userdetails.UserDetails (java.lang.String is in module java.base of loader 'bootstrap'; org.springframework.security.core.userdetails.UserDetails is in unnamed module of loader 'app')
Here are the classes:
JwtConfig
@Configuration
@ConfigurationProperties(prefix = "application.jwt")
public class JwtConfig {
private String secretKey;
private String tokenPrefix;
private String tokenExpirationAfterDays;
public JwtConfig() {
this.secretKey = System.getenv("SECRET_KEY");
}
public String getSecretKey() {
return secretKey;
}
public String getTokenPrefix() {
return tokenPrefix;
}
public void setTokenPrefix(String tokenPrefix) {
this.tokenPrefix = tokenPrefix;
}
public String getTokenExpirationAfterDays() {
return tokenExpirationAfterDays;
}
public void setTokenExpirationAfterDays(String tokenExpirationAfterDays) {
this.tokenExpirationAfterDays = tokenExpirationAfterDays;
}
}
JwtUtil
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import java.time.LocalDate;
import java.util.stream.Collectors;
public class JwtUtil {
private final JwtConfig jwtConfig;
@Autowired
public JwtUtil(JwtConfig jwtConfig) {
this.jwtConfig = jwtConfig;
}
public String getAccessToken(UserDetails user) {
Algorithm algorithm = Algorithm.HMAC256(jwtConfig.getSecretKey());
return JWT.create()
.withSubject(user.getUsername())
.withExpiresAt(java.sql.Date.valueOf(LocalDate.now().plusDays(1)))
.withIssuer("auth0")
.withClaim("roles", user.getAuthorities().stream().map(GrantedAuthority::getAuthority).collect(Collectors.toList()))
.sign(algorithm);
}
public String getRefreshToken(UserDetails user) {
Algorithm algorithm = Algorithm.HMAC256(jwtConfig.getSecretKey());
return JWT.create()
.withSubject(user.getUsername())
.withExpiresAt(java.sql.Date.valueOf(LocalDate.now().plusWeeks(4)))
.withIssuer("auth0")
.withClaim("roles", user.getAuthorities().stream().map(GrantedAuthority::getAuthority).collect(Collectors.toList()))
.sign(algorithm);
}
}
AuthenticationFilter
import org.springframework.security.core.userdetails.User;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
public class AuthenticationFilter extends UsernamePasswordAuthenticationFilter {
private final AuthenticationManager authenticationManager;
private final JwtConfig jwtConfig;
@Autowired
public AuthenticationFilter(AuthenticationManager authenticationManager, JwtConfig jwtConfig) {
this.authenticationManager = authenticationManager;
this.jwtConfig = jwtConfig;
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
String username = request.getParameter("username");
String password = request.getParameter("password");
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, password);
return authenticationManager.authenticate(authenticationToken);
}
@Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authentication) throws IOException, ServletException {
UserMediator userMediator = (UserMediator) authentication.getPrincipal();
JwtUtil jwtUtil = new JwtUtil(jwtConfig);
Map<String, String> tokens = new HashMap<>();
tokens.put("access_token", jwtUtil.getAccessToken(userMediator));
tokens.put("refresh_token", jwtUtil.getRefreshToken(userMediator));
response.setContentType(APPLICATION_JSON_VALUE);
new ObjectMapper().writeValue(response.getOutputStream(), tokens);
}
}
AuthorizationFilter
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.reddit.jwt.JwtConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.security.sasl.AuthenticationException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class AuthorizationFilter extends OncePerRequestFilter {
private final JwtConfig jwtConfig;
@Autowired
public AuthorizationFilter(JwtConfig jwtConfig) {
this.jwtConfig = jwtConfig;
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
if (request.getServletPath().equals("/api/login") || request.getServletPath().equals("/api/token/refresh")) {
filterChain.doFilter(request, response);
} else {
String authorizationHeader = request.getHeader(AUTHORIZATION);
if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) {
try {
String token = authorizationHeader.substring("Bearer ".length());
Algorithm algorithm = Algorithm.HMAC256(jwtConfig.getSecretKey());
JWTVerifier verifier = JWT.require(algorithm).build();
DecodedJWT decodedJWT = verifier.verify(token);
String username = decodedJWT.getSubject();
String[] roles = decodedJWT.getClaim("roles").asArray(String.class);
Collection<SimpleGrantedAuthority> authorities = new ArrayList<>();
stream(roles).forEach(role -> {
authorities.add(new SimpleGrantedAuthority(role));
});
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, null, authorities);
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
filterChain.doFilter(request, response);
} catch (Exception exception) {
response.setStatus(FORBIDDEN.value());
Map<String, String> error = new HashMap<>();
error.put("error_message", exception.getMessage());
response.setContentType(APPLICATION_JSON_VALUE);
new ObjectMapper().writeValue(response.getOutputStream(), error);
}
} else {
filterChain.doFilter(request, response);
}
}
}
}
UserDetailServiceImpl
package com.reddit.service;
import com.reddit.entity.User;
import com.reddit.repository.UserRepository;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
@Service
// @Transactional
public class UserDetailServiceImpl implements UserService, UserDetailsService {
private final UserRepository userRepository;
@Autowired
public UserDetailServiceImpl(UserRepository userRepository) {
this.userRepository = userRepository;
}
@Override
@Transactional
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Optional<User> userOptional = userRepository.findUserByUsername(username);
User user = userOptional
.orElseThrow(() -> new UsernameNotFoundException("No user found with username: " + username));
return new UserMediator(user);
}
private Collection<? extends GrantedAuthority> getAuthorities(String role) {
return Collections.singletonList(new SimpleGrantedAuthority(role));
}
@Override
@Transactional
public List<User> getUsers() {
return (List<User>) userRepository.findAll();
}
@Override
public User getUser(String username) {
Optional<User> user = userRepository.findUserByUsername(username);
return user.orElse(null);
}
@Override
public User deleteUser(User user) {
userRepository.delete(user);
return user;
}
@Transactional
public boolean isUsernameAlreadyInUse(String username) {
return userRepository.existsUserByUsername(username);
}
@Transactional
public boolean isEmailAlreadyInUse(String email) {
return userRepository.existsUserByEmail(email);
}
}
AuthenticationServiceImpl
import com.reddit.entity.User;
@Service
public class AuthenticationServiceImpl implements AuthenticationService {
private final UserRepository userRepository;
private final PasswordEncoder passwordEncoder;
@Autowired
public AuthenticationServiceImpl(UserRepository userRepository, PasswordEncoder passwordEncoder) {
this.userRepository = userRepository;
this.passwordEncoder = passwordEncoder;
}
@Override
public void saveUser(RegistrationRequest registrationRequest) {
User user = new User(registrationRequest.getFirstName(),
registrationRequest.getLastName(),
registrationRequest.getEmail(),
registrationRequest.getUsername(),
passwordEncoder.encode(registrationRequest.getPassword()),
registrationRequest.getCountry(),
true);
userRepository.save(user);
}
}
UserMediator
import com.reddit.entity.User;
import org.springframework.security.core.userdetails.UserDetails;
public class UserMediator implements UserDetails {
private User user;
public UserMediator(User user) {
this.user = user;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return new ArrayList<>();
}
@Override
public String getPassword() {
return user.getPassword();
}
@Override
public String getUsername() {
return user.getUsername();
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return user.isEnabled();
}
}
Originally I didn't have UserMediator class, I made it because I saw it in a post with the same problem.
I checked the imports and the classes are imported right.
I have no idea why is this exception happening because Spring is not showing me the stack trace. Any help is welcomed.
Update:
Stack trace: stack trace
Stack trace copied: https://pastebin.com/SQWg9Dpy
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|