【问题标题】:JUnit 5 Spring Security WebMvcTest no bean of type PasswordEncoderJUnit 5 Spring Security WebMvcTest 没有 PasswordEncoder 类型的 bean
【发布时间】:2021-06-23 11:27:44
【问题描述】:

我正在尝试测试一个返回百里香模板的基本页面控制器。 控制器如下所示:

@Controller
public class EntryOverviewController {

    @GetMapping(ApplicationConstants.URL_ENTRY_OVERVIEW)
    public String getPage(Model model) {

        return ApplicationConstants.VIEW_ENTRY_OVERVIEW;
    }

我的 WebSecurityConfig 如下所示:

@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
@Slf4j
@Configuration
@Order(1005)
public class WebSecurityConfig {

    @Configuration
    @Order(1005)
    public class WebAppSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {

        @Override
        protected void configure(HttpSecurity http) throws Exception {

            http.authorizeRequests().antMatchers("/**").permitAll().and().headers().defaultsDisabled().cacheControl().and()
                    .httpStrictTransportSecurity().includeSubDomains(false).maxAgeInSeconds(31536000).and().frameOptions().disable().and()
                    .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.ALWAYS);
        }
    }

    @Order(1004)
    @Configuration
    public static class ActuatorWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {

        @Autowired
        private PasswordEncoder passwordEncoder;

        @Value("${management.endpoints.web.access.user}")
        private String user;

        @Value("${management.endpoints.web.access.password}")
        private String password;

        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.requestMatcher(EndpointRequest.toAnyEndpoint()).authorizeRequests().requestMatchers(EndpointRequest.to(HealthEndpoint.class))
                    .permitAll().anyRequest().authenticated().and().httpBasic().and().sessionManagement()
                    .sessionCreationPolicy(SessionCreationPolicy.STATELESS);
        }

        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
            auth.inMemoryAuthentication().withUser(user).password(passwordEncoder.encode(password)).roles();
        }
    }

    /** Password encoder */
    @Bean(name = "passwordEncoder")
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

我的测试是这样的:

@ExtendWith(SpringExtension.class)
@WebMvcTest(EntryOverviewController.class)
class EntryOverviewControllerTest {

    @Autowired
    private MockMvc mvc;

    @WithMockUser(value = "user")
    @Test
    public void testCorrectModel() throws Exception {

      mvc.perform(get(ApplicationConstants.URL_ENTRY_OVERVIEW)).andExpect(status().isOk()).andExpect(model().attributeExists("registerEntry"))
                .andExpect(view().name(ApplicationConstants.VIEW_ENTRY_OVERVIEW));
    }

}

现在,当我想执行 junit 5 测试时,它失败并显示错误消息 org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'org.springframework.security.crypto.password.PasswordEncoder' available:预计至少有 1 个 bean 有资格作为 autowire 候选者。依赖注解:{@org.springframework.beans.factory.annotation.Autowired(required=true)}

当我使用 @MockBean 模拟 passwordEncoder 时,它给出了错误密码不能为空。

【问题讨论】:

    标签: java spring spring-boot junit5


    【解决方案1】:

    不用自动装配 PasswordEncoder,只需使用 passwordEncoder()。在此处查看 bean 间依赖关系 https://docs.spring.io/spring-framework/docs/3.2.x/spring-framework-reference/html/beans.html#beans-java-configuration-annotation

    【讨论】:

      【解决方案2】:

      您是否有理由要命名编码器 bean?您没有多个 passwordEncoder 实现。

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

      我认为 spring 在模拟此实现时存在问题。相反,你可以有

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

      它应该可以工作。

      【讨论】:

        【解决方案3】:

        我通过简单地更改类注释来解决它。

        @WebMvcTest(EntryOverviewController.class)
        @Import(WebSecurityConfig.class)
        class EntryOverviewControllerTest {
        ...
        }
        

        所以我添加了@Import 语句并且它起作用了

        【讨论】:

          猜你喜欢
          • 2018-09-14
          • 2018-08-13
          • 2015-03-27
          • 1970-01-01
          • 2020-10-27
          • 2021-08-30
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多