Spring Boot报错:需定义org.springframework.security.core.userdetails.UserDetailsService Bean
解决Spring Boot中UserDetailsService Bean未找到的问题
问题描述
运行Spring Boot应用时抛出错误:提示需定义类型为org.springframework.security.core.userdetails.UserDetailsService的Bean。
错误详情
dev.spring.hibernatedemo.security.JwtAuthenticationFilter构造函数的第0个参数需要该类型的Bean,但容器中未找到。Spring建议在配置中定义org.springframework.security.core.userdetails.UserDetailsService类型的Bean。
相关代码
SecurityConfig.java
package dev.spring.hibernatedemo.config; import dev.spring.hibernatedemo.security.JwtAuthenticationEntryPoint; import dev.spring.hibernatedemo.security.JwtAuthenticationFilter; import lombok.AllArgsConstructor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; @Configuration @AllArgsConstructor public class SecurityConfig { private JwtAuthenticationFilter jwtAuthenticationFilter; private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint; @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { return http .cors(AbstractHttpConfigurer::disable) .csrf(AbstractHttpConfigurer::disable) .authorizeHttpRequests((auth) -> auth.requestMatchers("/authenticate").permitAll().anyRequest().authenticated()) .exceptionHandling(ex -> ex.authenticationEntryPoint(jwtAuthenticationEntryPoint)) .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class) .build(); } @Bean public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception { return authenticationConfiguration.getAuthenticationManager(); } }
JwtAuthenticationFilter.java
package dev.spring.hibernatedemo.security; import io.jsonwebtoken.ExpiredJwtException; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; import org.springframework.stereotype.Component; import org.springframework.web.filter.OncePerRequestFilter; import java.io.IOException; @Component @Slf4j public class JwtAuthenticationFilter extends OncePerRequestFilter { private final JwtHelper jwtHelper; private final UserDetailsService userDetailsService; public JwtAuthenticationFilter(UserDetailsService userDetailsService, JwtHelper jwtHelper) { this.jwtHelper = jwtHelper; this.userDetailsService = userDetailsService; } @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { String requestHeader = request.getHeader("Authentication"); //Bearer log.info("Header: {}", requestHeader); String username = null; String token = null; if (requestHeader != null && requestHeader.startsWith("Bearer")) { token = requestHeader.substring(7); try { username = jwtHelper.getUsernameFromToken(token); } catch (IllegalArgumentException e) { logger.info("Illegal Argument while fetching the username !!"); logger.error(e.getMessage()); } catch (ExpiredJwtException e) { logger.info("Given jwt token is expired !!"); logger.error(e.getMessage()); } catch (Exception e) { logger.error(e.getMessage()); } } else { logger.info("Invalid header!!"); } if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) { UserDetails userDetails = this.userDetailsService.loadUserByUsername(username); Boolean validatedToken = this.jwtHelper.validateToken(token, userDetails); if (validatedToken) { UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities()); authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); SecurityContextHolder.getContext().setAuthentication(authenticationToken); } else { logger.info("Validation failed!!!"); } } filterChain.doFilter(request, response); } }
解决方案
Spring Security需要一个实现UserDetailsService接口的类来加载用户认证信息,当前项目缺失该Bean,需补充如下:
1. 创建自定义UserDetailsService实现类
package dev.spring.hibernatedemo.service; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.stereotype.Service; import java.util.ArrayList; @Service public class CustomUserDetailsService implements UserDetailsService { @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { // 开发阶段示例:返回固定用户,生产环境需替换为数据库查询逻辑 // {noop}表示不加密密码,生产环境建议使用BCryptPasswordEncoder加密 return new User("admin", "{noop}admin123", new ArrayList<>()); } }
2. 关键说明
- 该类标注
@Service,会被Spring自动扫描并注册为Bean,解决依赖缺失问题 loadUserByUsername方法需实现用户信息的加载逻辑,示例中用了固定用户,实际项目要连接数据库查询真实用户数据- 确保该类所在包被Spring组件扫描覆盖(主类包为
dev.spring.hibernatedemo时,子包service会被自动扫描)
内容的提问来源于stack exchange,提问作者cratos
相关产品推荐
相关产品推荐

