【问题标题】:Spring boot 2+ Could not Autowire. There is more than one bean of 'UserDetailsService'Spring Boot 2+ 无法自动装配。 'UserDetailsS​​ervice' 的 bean 不止一个
【发布时间】:2019-11-04 05:48:10
【问题描述】:

大家好,我是 spring security 和 jwt 的新手。我在我的 spring boot 项目中实现 Jwt 以保护用户登录,我正在使用 spring boot 2.1.5 而且我对 spring boot 2+ 中的新 bean 限制了解不多。 我需要一些帮助..这里我正在尝试@Autowired UserDetailsS​​ervice 并且代码运行良好..结果也很好..但intellij在

处显示错误

@Autowired UserDetailsS​​ervice jwtUserDetailsS​​ervice

说...无法自动装配。 UserDetailsS​​ervice 类型的 bean 不止一种。

谁能解释一下这里发生了什么错误,为什么我不能自动装配,为什么以及 spring boot 2+ 中的自动装配限制是什么?

提前致谢

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurity extends WebSecurityConfigurerAdapter {

    @Autowired
    private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Autowired
    private JwtFilter jwtFilter;

    @Autowired
    private UserDetailsService jwtUserDetailsService; // here i got error only

    @Autowired
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(jwtUserDetailsService).passwordEncoder(passwordEncoder);
    }

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable();
        http.authorizeRequests().antMatchers("/api/user/add", "/generate").permitAll().anyRequest().authenticated().and()            .exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint)
                .and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
        http.addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class);
    }
}

我的 customUserDetailService 是

@Service
public class JwtUserDetailService implements UserDetailsService {

    @Autowired
    private UserRepository userRepository;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = userRepository.findByUsername(username);
        if (user != null) {
            return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(), new ArrayList<>());
        } else {
            throw new UsernameNotFoundException("Username does't exists");
        }

    }
}

我的 JwtController 类,它暴露了重新端点以生成 jwt 令牌


@CrossOrigin
@RestController
public class JwtController {

    @Autowired
    private JwtUtils jwtUtils;

    @Autowired
    private AuthenticationManager authenticationManager;
    @Autowired
    private JwtUserDetailService jwtUserDetailService;

    @PostMapping(value = "/generate")
    public ResponseEntity<?> generateToken(@RequestBody JwtRequest jwtRequest) throws Exception {
        try {
            authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(jwtRequest.getUsername(),
                    jwtRequest.getPassword()));
        } catch (DisabledException e) {
            throw new Exception("USER_DISABLED", e);
        } catch (BadCredentialsException e) {
            throw new Exception("INVAILD_CREDENTIALS", e);
        }
        final UserDetails userDetails = jwtUserDetailService.loadUserByUsername(jwtRequest.getUsername());
        final String token = jwtUtils.generateToken(userDetails);

        return ResponseEntity.ok(new JwtResponse(token));
    }
}

我的 JwtFilter 类

@Component
public class JwtFilter extends OncePerRequestFilter {

    @Autowired
    private JwtUserDetailService jwtUserDetailService;

    @Autowired
    private JwtUtils jwtUtils;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
            throws ServletException, IOException {

        final String requestTokenHeader = request.getHeader("Authorization");

        String username = null;
        String jwtToken = null;

        if (requestTokenHeader != null && requestTokenHeader.startsWith("Bearer ")) {
            jwtToken = requestTokenHeader.substring(7);
            try {
                username = jwtUtils.getUsernameFromToken(jwtToken);
            } catch (IllegalArgumentException e) {
                System.out.println("Unable to get JWT Token");
            } catch (ExpiredJwtException e) {
                System.out.println("JWT Token has expired");
            }
        } else {
            logger.warn("JWT Token does not begin with Bearer String");
        }

        if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {

            UserDetails userDetails = this.jwtUserDetailService.loadUserByUsername(username);

            if (jwtUtils.validate(jwtToken, userDetails)) {

                UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(
                        userDetails, null, userDetails.getAuthorities());
                usernamePasswordAuthenticationToken
                        .setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
               SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
            }
        }
        chain.doFilter(request, response);
    }

}

其他正常的东西,例如实体、存储库和一些安全的重新端点

【问题讨论】:

  • 好像你已经创建了两个UserDetailsS​​ervice组件jwtUserDetailsS​​ervice。你能确认一下吗?
  • @PratikAmbani 不只是一个,一切都运行良好
  • 似乎spring-boot-starter-security 包含UserDetailsService 的一些默认实现。您可以在 JwtUserDetailService 上添加 @Primary 注释,告诉 Spring 它必须使用您的实现。

标签: spring-boot spring-mvc spring-security jwt userdetailsservice


【解决方案1】:

你可以把这段代码放在application.properties中:

spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration

【讨论】:

    【解决方案2】:

    我在另一个上下文中遇到了同样的错误。原因是 Idea 不知道要使用哪个 'UserDetailsS​​ervice' 类型的 bean。 我的解决方案是通过注释Qualifier

    @Qualifier("beanNameWhichYouWantUse")
    @Autowired
    private UserDetailsService jwtUserDetailsService;
    

    如果使用 Idea: 将鼠标指向错误,请从上下文菜单中选择:

    "More actions" -> "Add qualifier"
    

    最后选择 bean

    【讨论】:

      【解决方案3】:

      UserDetailsS​​ervice 由 spring 提供。 要自动装配,您需要对其进行配置。

      @Bean
      public UserDetailsService getUserDetails(){
         return new JwtUserDetailService(); // Implementation class
      }
      

      如果您对 Bean 配置不感兴趣。 您可以直接自动装配 JwtUserDetailService。

      @Autowired
      private JwtUserDetailService jwtUserDetailsService;
      

      【讨论】:

      • 谢谢我明白了,但昨天我试过了,我的令牌生成了,但是当我使用那个令牌检索数据时,我收到一个错误,说无效令牌..但现在它工作正常不要知道为什么:D
      猜你喜欢
      • 2016-03-29
      • 1970-01-01
      • 2015-09-14
      • 1970-01-01
      • 2014-08-22
      • 2020-02-28
      • 2014-09-03
      • 2015-04-07
      • 2012-12-07
      相关资源
      最近更新 更多