升级我的应用程序的SecurityConfig,我得到了工作版本,但破坏了这么多的测试。
当前弃用版本为here,文件之间的差异如下所示
新版本的效果很好(我和 Postman 一起试过了)。
与差异版本我得到了每一个测试通过100%,现在我有几个错误,我不能解决自己。
例如,该测试:
@WebMvcTest(PingController.class)
class AuthTokenFilterTest {
@Autowired
private MockMvc mvc;
@Autowired
AuthTokenFilter authTokenFilter;
@MockBean
JwtUtils jwtUtils;
@MockBean
UserDetailsServiceImpl userDetailsServiceImpl;
@MockBean
UserDetails userDetails;
@MockBean
private PingService pingService;
@MockBean
AuthEntryPointJwt authEntryPointJwt;
@MockBean
UsersRepository usersRepository;
@Test
void testCanReturnNullIfJwtIsMissing() throws Exception {
mvc.perform(get("/api/v1/ping")).andExpect(status().isOk());
}
/**
* Test can validate the token.
*
* Use /ping route because it is the route out of security, so we can
* concentrate to the AuthTokenFilter class.
*
* @throws Exception
*/
@Test
void testCanValidateToken() throws Exception {
String token = "a1.b2.c3";
when(jwtUtils.validateJwtToken(token)).thenReturn(true);
when(jwtUtils.getUserNameFromJwtToken(token)).thenReturn("username");
when(userDetailsServiceImpl.loadUserByUsername("username")).thenReturn(userDetails);
when(userDetails.getAuthorities()).thenReturn(null);
mvc.perform(get("/api/v1/ping").header("Authorization", "Bearer " + token)).andExpect(status().isOk());
}
/**
* Test cannot validate the token.
*
* Use /ping route because it is the route out of security, so we can
* concentrate to the AuthTokenFilter class.
*
* @throws Exception
*/
@Test
void testCannotValidateToken() throws Exception {
String token = "a1.b2.c3";
when(jwtUtils.validateJwtToken(token)).thenReturn(false);
mvc.perform(get("/api/v1/ping").header("Authorization", "Bearer " + token)).andExpect(status().isOk());
}
/**
* Test cannot validate the token if the header is missing.
*
* Use /ping route because it is the route out of security, so we can
* concentrate to the AuthTokenFilter class.
*
* @throws Exception
*/
@Test
void testCanReturnNullIfJwtIsMissingButOtherHeaderIsInPlace() throws Exception {
String token = "a1.b2.c3";
mvc.perform(get("/api/v1/ping").header("Authorization", "NotStartWithBearer " + token))
.andExpect(status().isOk());
}
}
我得到
第一个月
(The测试为here)
或者,在寄存器控制器测试中
@WebMvcTest(AuthController.class)
@TestPropertySource(locations = "classpath:application.properties", properties = "app.enableSubscription=false")
class RegisterControllerTest {
@Autowired
private MockMvc mvc;
@Autowired
AuthTokenFilter authTokenFilter;
@MockBean
AuthEntryPointJwt authEntryPointJwt;
@MockBean
JwtUtils jwtUtils;
@Autowired
private ObjectMapper objectMapper;
@MockBean
UserDetailsServiceImpl userDetailsServiceImpl;
@MockBean
AuthenticationManager authenticationManager;
@MockBean
Authentication authentication;
@MockBean
SecurityContext securityContext;
@MockBean
private RolesService rolesService;
@Test
@WithMockUser(username = "username", authorities = { "ROLE_ADMIN" })
void cannotRegisterUserWhenSubscriptionsAreDisabled() throws Exception {
var userToSave = validUserEntity("username", "password");
var savedUser = validUserEntity("username", "a1.b2.c3");
when(userDetailsServiceImpl.post(userToSave)).thenReturn(savedUser);
mvc.perform(post("/api/v1/auth/register/").contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsBytes(userToSave))).andExpect(status().isBadRequest())
.andExpect(jsonPath("$.status", is("error")))
.andExpect(jsonPath("$.message", is("subscriptions disabled")));
}
private static UsersEntity validUserEntity(String username, String password) {
return UsersEntity.builder().username(username).password(password).build();
}
}
我得到Error creating bean with name 'authController': Unsatisfied dependency expressed through field 'passwordEncoder'
安全配置
旧版本:
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Autowired
UserDetailsServiceImpl userDetailsServiceImpl;
@Autowired
private AuthEntryPointJwt authEntryPointJwt;
@Bean
AuthTokenFilter authenticationJwtTokenFilter() {
return new AuthTokenFilter();
}
@Value("${app.allowedOrigins}")
private String allowedOriginsFromApplicationProperties;
/**
* Return allowedOrigins from application properties
*/
private String getAllowedOriginsFromApplicationProperties() {
return this.allowedOriginsFromApplicationProperties;
}
/**
* Return the allowed origins.
*
* @return
*/
private List<String> getAllowedOrigins() {
String[] allowedOrigins = this.getAllowedOriginsFromApplicationProperties().split(",");
return Arrays.asList(allowedOrigins);
}
@Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Bean
PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
public void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
authenticationManagerBuilder.userDetailsService(userDetailsServiceImpl).passwordEncoder(passwordEncoder());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable().exceptionHandling().authenticationEntryPoint(authEntryPointJwt).and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().authorizeRequests()
.mvcMatchers("/api/v1/ping").permitAll().mvcMatchers("/api/v1/auth/register").permitAll()
.mvcMatchers("/api/v1/auth/login").permitAll().mvcMatchers("/api-docs/**").permitAll()
.mvcMatchers("/swagger-ui/**").permitAll().mvcMatchers("/swagger-ui.html").permitAll()
.mvcMatchers("/documentation").permitAll().mvcMatchers("/").permitAll().anyRequest().authenticated();
http.addFilterBefore(authenticationJwtTokenFilter(), UsernamePasswordAuthenticationFilter.class);
}
@Bean
CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.setAllowedOriginPatterns(getAllowedOrigins());
config.addAllowedHeader("*");
config.addAllowedMethod("*");
source.registerCorsConfiguration("/**", config);
return new CorsFilter(source);
}
}
和新的
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
@RequiredArgsConstructor
public class SecurityConfiguration {
@Autowired
UserDetailsServiceImpl userDetailsServiceImpl;
@Autowired
AuthEntryPointJwt authEntryPointJwt;
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration)
throws Exception {
return authenticationConfiguration.getAuthenticationManager();
}
@Bean
AuthTokenFilter authenticationJwtTokenFilter() {
return new AuthTokenFilter();
}
@Value("${app.allowedOrigins}")
private String allowedOriginsFromApplicationProperties;
/**
* Return allowedOrigins from application properties
*/
private String getAllowedOriginsFromApplicationProperties() {
return this.allowedOriginsFromApplicationProperties;
}
/**
* Return the allowed origins.
*
* @return
*/
private List<String> getAllowedOrigins() {
String[] allowedOrigins = this.getAllowedOriginsFromApplicationProperties().split(",");
return Arrays.asList(allowedOrigins);
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable().exceptionHandling().authenticationEntryPoint(authEntryPointJwt).and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().authorizeRequests()
.mvcMatchers("/api/v1/ping").permitAll().mvcMatchers("/api/v1/auth/register").permitAll()
.mvcMatchers("/api/v1/auth/login").permitAll().mvcMatchers("/api-docs/**").permitAll()
.mvcMatchers("/swagger-ui/**").permitAll().mvcMatchers("/swagger-ui.html").permitAll()
.mvcMatchers("/documentation").permitAll().mvcMatchers("/").permitAll().anyRequest().authenticated();
http.addFilterBefore(authenticationJwtTokenFilter(), UsernamePasswordAuthenticationFilter.class);
return http.build();
}
@Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**").allowedMethods("*");
}
};
}
@Bean
CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.setAllowedOriginPatterns(getAllowedOrigins());
config.addAllowedHeader("*");
config.addAllowedMethod("*");
source.registerCorsConfiguration("/**", config);
return new CorsFilter(source);
}
}
1条答案
按热度按时间cgh8pdjw1#
我引用documentation for
@WebMvcTest
:可用于仅关注Spring MVC组件的Spring MVC测试的注解。
使用此注解将禁用完全自动配置,而仅应用与MVC测试相关的配置(即@Controller、@ControllerAdvice、@JsonComponent、Converter/GenericConverter、Filter、WebMvcConfigurer和HandlerMethodArgumentResolver Bean,但不应用@Component、@Service或@Repository Bean)。
这意味着您的
SecurityConfiguration
永远不会创建,因为Spring只关注Web层,而不是您的定制组件。您有两个选项可以修复此问题:
1.导入必要的Bean
注意,您还需要导入
SecurityConfiguration
所依赖的配置/组件,以及这些bean所依赖的所有类,等等。因此,它更有可能是这样的:但是这会降低测试的可维护性,如果你添加了一个新的依赖项,比如
UserDetailsServiceImpl
,你的测试会因为找不到bean而再次中断。2.使用
@SpringBootTest
@SpringBootTest
将创建整个应用程序上下文,并为测试做一些细微的修改。这种方法的缺点是测试比较慢,因为要创建整个应用程序上下文,而不仅仅是Web层。我更喜欢这种方法,因为我认为可维护性比测试的执行速度更重要。