I'm writing test for a Rest API controller. This endpoint is accessible without any authorization:
@EnableWebSecurity @Configuration @Import(AppConfig.class) class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private UserDetailsRepository accountRepository; @Autowired private CustomUserDetailsService customUserDetailsService; @Autowired private JWTAuthenticationFilter jwtAuthenticationFilter; @Override protected void configure(HttpSecurity http) throws Exception { http .csrf().disable() .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class) .authorizeRequests() .anyRequest().authenticated().and() .sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.STATELESS); } /* * Apparently, permitAll() doesn't work for custom filters, therefore we ignore the signup and login endpoints * here */ @Override public void configure(WebSecurity web) throws Exception { web.ignoring() .antMatchers(HttpMethod.POST, "/login") .antMatchers(HttpMethod.POST, "/signup"); } /* * set user details services and password encoder */ @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userDetailsServiceBean()).passwordEncoder(passwordEncoder()); } @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } /* Stopping spring from adding filter by default */ @Bean public FilterRegistrationBean rolesAuthenticationFilterRegistrationDisable(JWTAuthenticationFilter filter) { FilterRegistrationBean registration = new FilterRegistrationBean(filter); registration.setEnabled(false); return registration; }
}
The JWTAuthenticationFilter
class:
@Component public class JWTAuthenticationFilter extends AbstractAuthenticationProcessingFilter { @Autowired private UserDetailsService customUserDetailsService; private static Logger logger = LoggerFactory.getLogger(JWTAuthenticationFilter.class); private final static UrlPathHelper urlPathHelper = new UrlPathHelper(); final static String defaultFilterProcessesUrl = "/**"; public JWTAuthenticationFilter() { super(defaultFilterProcessesUrl); super.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher(defaultFilterProcessesUrl)); //Authentication will only be initiated for the request url matching this pattern setAuthenticationManager(new NoOpAuthenticationManager()); } @Override public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException { Authentication authentication = AuthenticationService.getAuthentication(request, customUserDetailsService); return getAuthenticationManager().authenticate(authentication); } @Override protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException { logger.debug("failed authentication while attempting to access "+ urlPathHelper.getPathWithinApplication((HttpServletRequest) request)); response.sendError(HttpServletResponse.SC_UNAUTHORIZED,"Authentication Failed"); } @Override protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException { SecurityContextHolder.getContext().setAuthentication(authResult); chain.doFilter(request, response); } @Override public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { super.doFilter(req, res, chain); } }
When I make a request (using postman) to 'signup' endpoint it works fine. But when I run the test, it hits doFilter
and fails, as it doesn't get authenticated.
@RunWith(SpringRunner.class) @SpringBootTest @AutoConfigureMockMvc public class AuthenticationControllerFTest { @Autowired private MockMvc mockMvc; @MockBean private AuthenticationManager authenticationManager; @Test public void testCreate() throws Exception { Authentication authentication = Mockito.mock(Authentication.class); Mockito.when(authentication.getName()).thenReturn("DUMMY_USERNAME"); Mockito.when( authenticationManager.authenticate(Mockito .any(UsernamePasswordAuthenticationToken.class))) .thenReturn(authentication); String exampleUserInfo = "{\"name\":\"Test1234\",\"username\":\"test@test.com\",\"password\":\"Salam12345\"}"; RequestBuilder requestBuilder = MockMvcRequestBuilders .post("/signup") .accept(MediaType.APPLICATION_JSON).content(exampleUserInfo) .contentType(MediaType.APPLICATION_JSON); MvcResult result = mockMvc.perform(requestBuilder).andReturn(); MockHttpServletResponse response = result.getResponse(); int status = response.getStatus(); String content = response.getContentAsString(); System.out.println(content); Assert.assertEquals("http response status is wrong", 200, status); } }
Any idea on how to fix this issue ?
2 Answers
Answers 1
The issue was resolved by adding the following code to the test class:
@Autowired private WebApplicationContext context; @Autowired private Filter springSecurityFilterChain; @Before public void setup() { mockMvc = MockMvcBuilders.webAppContextSetup(context) .addFilters(springSecurityFilterChain).build(); }
Answers 2
@Override protected void configure(HttpSecurity http) throws Exception { http.csrf().disable().authorizeRequests() .antMatchers("/**").permitAll() .anyRequest().authenticated(); }
0 comments:
Post a Comment