I don’t know what I did wrong. I was trying to implement the Jwt token(only post method).it shows the exception that
Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.ClassCastException: class com.vivek.discussion.service.UserDetailServiceImpl$1 cannot be cast to class com.vivek.discussion.model.User (com.vivek.discussion.service.UserDetailServiceImpl$1 and com.vivek.discussion.model.User are in unnamed module of loader ‘app’)] with root cause
java.lang.ClassCastException: class com.vivek.discussion.service.UserDetailServiceImpl$1 cannot be cast to class com.vivek.discussion.model.User (com.vivek.discussion.service.UserDetailServiceImpl$1 and com.vivek.discussion.model.User are in unnamed module of loader ‘app’) at com.vivek.discussion.security.JwtProvider.generateToken(JwtProvider.java:26) ~[classes/:na] at com.vivek.discussion.service.AuthService.login(AuthService.java:85) ~[classes/:na]
I am not sharing DTO classes. I know the problem is in Jwtprovider but no idea why
UserDetailService
package com.vivek.discussion.service;
@AllArgsConstructor
@Service
public class UserDetailServiceImpl implements UserDetailsService {
private final UserRepository userRepository;
@Override
public UserDetails loadUserByUsername(String username){
Optional<User> userOptional=userRepository.findByUsername(username);
User user=userOptional.orElseThrow(()-> new UsernameNotFoundException("User name not valid "+username));
return new UserDetails() {
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return UserDetailServiceImpl.this.getAuthorities("USER");
}
@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();
}
};
}
private Collection<? extends GrantedAuthority> getAuthorities(String role) {
return Collections.singletonList(new SimpleGrantedAuthority(role));
}
}
AuthService
package com.vivek.discussion.service;
@Service
@AllArgsConstructor
public class AuthService {
// constructor injection is recommended over direct autowired annotation
private final PasswordEncoder passwordEncoder;
private final UserRepository userRepository;
private final VerificationTokenRepository verificationTokenRepository;
private final MailService mailService;
private final AuthenticationManager authenticationManager;
private final JwtProvider jwtProvider;
@Transactional
public void signup(RegisterRequest registerRequest){
User user=new User();
user.setUsername(registerRequest.getUsername());
user.setPassword(passwordEncoder.encode(registerRequest.getPassword()));
user.setEmail(registerRequest.getEmail());
user.setCreated(Instant.now());
user.setEnabled(false);
userRepository.save(user);
String token=generateRandomToken(user);
mailService.sendMail(new NotificationEmail("Please Activate Your Account",user.getEmail(),"thank you For Signing In Into our disscusion form" +
"Please click the link below to get : "+
"http://localhost:8080/api/auth/accountVerification/"+token));
}
private String generateRandomToken (User user){
String s= UUID.randomUUID().toString();
VerificationToken verificationToken=new VerificationToken();
verificationToken.setToken(s);
verificationToken.setUser(user);
verificationTokenRepository.save(verificationToken);
return s;
}
public void verifyAccount(String token) {
Optional<VerificationToken> verificationToken= verificationTokenRepository.findAllByToken(token);
verificationToken.orElseThrow(() -> new SpringDiscussionException("Invalid Token"));
fetchUserAndUnable(verificationToken.get());
}
@Transactional
void fetchUserAndUnable(VerificationToken verificationToken) {
String username=verificationToken.getUser().getUsername();
User user=userRepository.findByUsername(username).orElseThrow(()-> new SpringDiscussionException("User not found with name : "+username));
user.setEnabled(true);
userRepository.save(user);
}
public AuthenticationResponse login(LoginRequest loginRequest) {
Authentication authentication=authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(loginRequest.getUsername(),loginRequest.getPassword()));
SecurityContextHolder.getContext().setAuthentication(authentication);
String token= jwtProvider.generateToken(authentication);
return new AuthenticationResponse(token,loginRequest.getUsername());
}
}
AuthController
@Slf4j
@RestController
@RequestMapping("/api/auth")
@AllArgsConstructor
public class AuthController {
private final AuthService authService;
@PostMapping("/signup")
public ResponseEntity<String> signup(@RequestBody RegisterRequest registerRequest){
log.info(registerRequest.getEmail());
authService.signup(registerRequest);
return new ResponseEntity<>("user registration link send", HttpStatus.OK );
}
@GetMapping("accountVerification/{token}")
public ResponseEntity<String> verify(@PathVariable String token){
authService.verifyAccount(token);
return new ResponseEntity<>("User is register",HttpStatus.OK);
}
@PostMapping("/login")
public AuthenticationResponse login(@RequestBody LoginRequest loginRequest){
return authService.login(loginRequest);
}
}
JwtProvider
package com.vivek.discussion.security;
import com.vivek.discussion.model.User;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.security.Keys;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import java.security.Key;
@Service
public class JwtProvider {
private Key key;
@PostConstruct
public void init(){
key= Keys.secretKeyFor(SignatureAlgorithm.HS256);
}
public String generateToken(Authentication authentication){
User principal=(User) authentication.getPrincipal();
return Jwts.builder().setSubject(principal.getUsername())
.signWith(key)
.compact();
}
}
pom.xml(only jwt dependency)
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.10.5</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<scope>runtime</scope>
<version>0.10.5</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<scope>runtime</scope>
<version>0.10.5</version>
</dependency>
SecurityConfig
package com.vivek.discussion.configuration;
import lombok.AllArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.BeanIds;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
@Configuration
@EnableWebSecurity
@AllArgsConstructor
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private final UserDetailsService userDetailsService;
@Bean(BeanIds.AUTHENTICATION_MANAGER)
@Override
public AuthenticationManager authenticationManagerBean() throws Exception{
return super.authenticationManagerBean();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/api/auth/**")
.permitAll()
.anyRequest()
.authenticated();
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
authenticationManagerBuilder.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
@Bean
PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
}
Advertisement
Answer
The problem is that your UserDetailsService
implementation is returning something of type UserDetails
:
@Override
public UserDetails loadUserByUsername(String username){
Optional<User> userOptional=userRepository.findByUsername(username);
User user=userOptional.orElseThrow(()-> new UsernameNotFoundException("User name not valid "+username));
return new UserDetails() {
But JwtProvider
is asking for something of type User
:
User principal=(User) authentication.getPrincipal();
Instead of returning an anonymous inner class, consider adding a copy constructor to User
and using a static inner class that extends your User
class and implements UserDetails
:
private static class InternalUser extends User implements UserDetails {
public InternalUser(User user) {
super(user); // call the copy constructor
}
// ... all the implemented methods from your anonymous class
}
Then, you can do in UserDetailsServiceImpl
:
@Override
public UserDetails loadUserByUsername(String username){
Optional<User> userOptional = userRepository.findByUsername(username);
User user = userOptional.orElseThrow(
() -> new UsernameNotFoundException("User not valid"));
return new InternalUser(user);
}