I'm writing integration tests for secured endpoints and they are all failing with 401 response. I am using JWT. for some reason mockmvc is not able to authenticate someone could help on what the issue is. here is the code
@ExtendWith(SpringExtension.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@AutoConfigureMockMvc
public class RestEndpointsIntegrationTest extends AbstractIntegrationTest {
@Autowired
private MockMvc mockMvc;
@Autowired
protected WebApplicationContext wac;
@Autowired
private FilterChainProxy springSecurityFilterChain;
@BeforeEach
public void setUp() {
this.mockMvc = MockMvcBuilders
.webAppContextSetup(wac)
.apply(springSecurity(springSecurityFilterChain))
.build();
}
@Test
void getAllTransactionsSuccessTest() throws Exception {
this.mockMvc.perform(get("/transactions/account?account_id=1")
//.with(user(AuthenticatedUser.builder().role("API_ADMIN").username("admin_user").build())))
.with(authentication(new UsernamePasswordAuthenticationToken(
"admin_user",
null,
Collections.singletonList(new SimpleGrantedAuthority("API_ADMIN"))
))))
.andDo(print())
.andExpect(status().isOk())
.andExpect(MockMvcResultMatchers.jsonPath("$.totalElements").value(50))
.andExpect(MockMvcResultMatchers.jsonPath("$.transactions").exists());
}
}
I have tried using @MockUser as well and still getting the 401 response
secuirty config class
@Configuration
@EnableWebSecurity
@EnableAutoConfiguration
public class WebSecurityConfig
extends WebSecurityConfigurerAdapter {
@Autowired
private JwtAuthenticationEntryPoint unauthorizedHandler;
@Autowired
private JwtAuthenticationProvider authenticationProvider;
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
((HttpSecurity)((HttpSecurity)((ExpressionUrlAuthorizationConfigurer.AuthorizedUrl)((ExpressionUrlAuthorizationConfigurer.AuthorizedUrl)((HttpSecurity)httpSecurity
.csrf().disable()).authorizeRequests()
.antMatchers("/transactions/all").hasRole("API_ADMIN")
.antMatchers(new String[]{"/auth/token","/v2/api-docs", "/configuration/ui","/swagger-resources", "/configuration/security", "/swagger-ui.html","/api/swagger-ui.html",
"/webjars/**", "/swagger-resources/**","/favicon.ico","/**/*.png","/**/*.gif","/**/*.svg","/**/*.jpg","/**/*.html","/**/*.css","/**/*.js"})).permitAll()
.anyRequest()).authenticated().and()).exceptionHandling()
.authenticationEntryPoint((AuthenticationEntryPoint)this.unauthorizedHandler)
.and()).sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
httpSecurity.cors();
httpSecurity.addFilterBefore((Filter)this.authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class);
httpSecurity.headers().cacheControl();
}
@Bean
@Override
public AuthenticationManager authenticationManager() throws Exception {
return new ProviderManager(Arrays.asList(new AuthenticationProvider[]{this.authenticationProvider}));
}
@Bean
public JwtAuthenticationTokenFilter authenticationTokenFilterBean() throws Exception {
JwtAuthenticationTokenFilter authenticationTokenFilter = new JwtAuthenticationTokenFilter();
authenticationTokenFilter.setAuthenticationManager(this.authenticationManager());
authenticationTokenFilter
.setAuthenticationSuccessHandler((AuthenticationSuccessHandler)new JwtAuthenticationSuccessHandler());
return authenticationTokenFilter;
}
}
CodePudding user response:
I see you use @Autowired
for getting autoconfiguring MockMvc
instance, but in a same time you configure it self in setUp()
You must remove self configuration for mockMvc and must use @WithMockUser
for using default test user configuration for tests (you can see defaults in WithMockUser.class
)
@WithMockUser
@ExtendWith(SpringExtension.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@AutoConfigureMockMvc
public class RestEndpointsIntegrationTest extends AbstractIntegrationTest {
@Autowired
private MockMvc mockMvc;
@Autowired
protected WebApplicationContext wac;
@Autowired
private FilterChainProxy springSecurityFilterChain;
@Test
void getAllTransactionsSuccessTest() throws Exception {
this.mockMvc.perform(get("/transactions/account?account_id=1"))
.andDo(print())
.andExpect(status().isOk())
.andExpect(MockMvcResultMatchers.jsonPath("$.totalElements").value(50))
.andExpect(MockMvcResultMatchers.jsonPath("$.transactions").exists());
}
}
if you need use different user attributes for testing you can specify @WithMockUser
self attributes (like username, password etc.)
Required dependencies
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
<scope>test</scope>
</dependency>
CodePudding user response:
- Other response is right on the need to remove your
setUp()
method (@Authowired MockMvc
is enough with@AutoConfigureMockMvc
) - You should not use
@WithMockUser
(which populates test security context withUsernamePasswordAuthenticationToken
instance, but either
@WithMockJwtAuth
from https://github.com/ch4mpy/spring-addonsmockMvc.perform(get(...).with(SecurityMockMvcRequestPostProcessors.jwt()...))
from spring-security-test
- If using
@SpringBootTest
, like you do, security config should be active, but if using@WebMvcTest
, you'll probably need to@Import
or@ComponentScan
it
You'll find complete usage sample here
Sample unit test with the two flavors (annotation and MockMvc post processor to help you choose):
@WebMvcTest(GreetingController.class)
@AutoConfigureSecurityAddons
@Import(SampleApi.WebSecurityConfig.class)
class GreetingControllerAnnotatedTest {
@Autowired
MockMvc api;
@Test
@WithMockJwtAuth(authorities = "ROLE_AUTHORIZED_PERSONNEL", claims = @OpenIdClaims(sub = "Ch4mpy", preferredUsername = "Tonton Pirate"))
void greetWithAnnotation() throws Exception {
api.perform(get("/greet")).andExpect(content().string("Hello Ch4mpy! You are granted with [ROLE_AUTHORIZED_PERSONNEL]."));
}
@Test
void greetWithPostProcessor() throws Exception {
api.perform(get("/greet/ch4mpy").secure(true).with(SecurityMockMvcRequestPostProcessors.jwt()
.authorities(List.of(new SimpleGrantedAuthority("ROLE_AUTHORIZED_PERSONNEL"))).jwt(jwt -> {
jwt.subject("Ch4mpy");
jwt.claims(claims -> claims.put(StandardClaimNames.PREFERRED_USERNAME, "Tonton Pirate"));
}))).andExpect(content().string("Hello Ch4mpy! You are granted with [ROLE_AUTHORIZED_PERSONNEL]."));
}