Spring Security类型转换异常:User无法转为CustomUserDetails
解决Spring Security中CustomUserDetails类型转换异常问题
问题场景
调用/change-password接口时抛出java.lang.ClassCastException,异常信息显示无法将org.springframework.security.core.userdetails.User转换为自定义的com.shopwaala.tenant.master.service.CustomUserDetails。
控制器代码
@RestController public class ChangePasswordController { @Autowired private UserService userService; @Autowired private PasswordEncoder passwordEncoder; @PostMapping("/change-password") public Response processChangePassword(@RequestParam String newPassword, @RequestParam String oldPass, @RequestParam long tenantId) throws ServletException { Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); CustomUserDetails details = (CustomUserDetails) authentication .getPrincipal(); User user = details.getUser(); if (oldPass.equals(newPassword)) { return new Response("Your new password must be different than the old one.", null, new Date(), HttpStatus.BAD_REQUEST.value()); } if (!passwordEncoder.matches(oldPass, user.getPassword())) { return new Response("Your old password is incorrect.", null, new Date(), HttpStatus.BAD_REQUEST.value()); } else { userService.changePassword(user, newPassword, tenantId); return new Response("password change sucessfully !", null, new Date(), HttpStatus.OK.value()); } } }
错误信息
2023-01-09 16:43:13 ERROR o.a.c.c.C.[.[.[.[dispatcherServlet] - Servlet.service() for servlet [dispatcherServlet] in context with path [/api/tenant-service] threw exception [Request processing failed; nested exception is java.lang.ClassCastException: class org.springframework.security.core.userdetails.User cannot be cast to class com.shopwaala.tenant.master.service.CustomUserDetails (org.springframework.security.core.userdetails.User and com.shopwaala.tenant.master.service.CustomUserDetails are in unnamed module of loader 'app')] with root cause java.lang.ClassCastException: class org.springframework.security.core.userdetails.User cannot be cast to class com.ABC.tenant.master.service.CustomUserDetails (org.springframework.security.core.userdetails.User and com.shopwaala.tenant.master.service.CustomUserDetails are in unnamed module of loader 'app') at com.shopwaala.auth.controller.ChangePasswordController.processChangePassword(ChangePasswordController.java:32)
解决方案
确保自定义UserDetailsService返回CustomUserDetails实例
你的UserDetailsService实现类必须返回自定义的CustomUserDetails,而不是Spring默认的User类。示例代码:@Service public class CustomUserDetailsService implements UserDetailsService { @Autowired private UserRepository userRepository; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { User user = userRepository.findByUsername(username) .orElseThrow(() -> new UsernameNotFoundException("用户不存在")); // 返回自定义CustomUserDetails实例 return new CustomUserDetails(user); } }在Security配置中指定自定义UserDetailsService
在Spring Security配置类中,明确指定使用你的CustomUserDetailsService,避免Spring自动装配默认实现:@Configuration @EnableWebSecurity public class SecurityConfig { @Autowired private CustomUserDetailsService userDetailsService; @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .authorizeHttpRequests(auth -> auth .anyRequest().authenticated() ) .formLogin(form -> form.permitAll()) .userDetailsService(userDetailsService); // 绑定自定义UserDetailsService return http.build(); } @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } }验证CustomUserDetails的正确实现
确保CustomUserDetails正确实现UserDetails接口,包含自定义业务方法(比如getUser()),并正确实现接口要求的所有方法:public class CustomUserDetails implements UserDetails { private final User user; public CustomUserDetails(User user) { this.user = user; } @Override public String getUsername() { return user.getUsername(); } @Override public String getPassword() { return user.getPassword(); } // 自定义方法,返回业务User对象 public User getUser() { return user; } // 实现UserDetails的其他必要方法 @Override public Collection<? extends GrantedAuthority> getAuthorities() { return user.getRoles().stream() .map(role -> new SimpleGrantedAuthority("ROLE_" + role.getName())) .collect(Collectors.toList()); } @Override public boolean isAccountNonExpired() { return true; } @Override public boolean isAccountNonLocked() { return true; } @Override public boolean isCredentialsNonExpired() { return true; } @Override public boolean isEnabled() { return true; } }排查其他认证流程问题
- 如果使用JWT等token认证方式,确保解析token时构建的Authentication对象的Principal是
CustomUserDetails实例。 - 检查项目中是否存在多个
UserDetailsServicebean,导致Spring选择了默认实现而非自定义类。
- 如果使用JWT等token认证方式,确保解析token时构建的Authentication对象的Principal是
内容的提问来源于stack exchange,提问作者Saif Quazi
相关产品推荐
相关产品推荐

