You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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实例。
    • 检查项目中是否存在多个UserDetailsService bean,导致Spring选择了默认实现而非自定义类。

内容的提问来源于stack exchange,提问作者Saif Quazi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 08:22:49