Ich möchte einen einfachen Test schreiben, aber meine SecurityConfig gibt mir immer Zugriff verweigert. Hier ist mein config:Mocking ein Benutzer im Frühjahr
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserService userService;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.addFilterBefore(new TokenAuthenticationFilter(userService), AnonymousAuthenticationFilter.class);
http.csrf().disable();
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.NEVER);
//Options preflight
http.authorizeRequests().antMatchers(HttpMethod.OPTIONS).permitAll();
//ACL
http.authorizeRequests().antMatchers(HttpMethod.POST, "/auth/password").hasAnyRole("ADMIN", "CUSTOMER", "REFUGEE");
http.authorizeRequests().antMatchers("/auth/**").anonymous();
//Tags
http.authorizeRequests().antMatchers(HttpMethod.GET, "/tags").hasAnyRole("ADMIN", "CUSTOMER", "REFUGEE");
http.authorizeRequests().antMatchers(HttpMethod.GET, "/tags/recommendation").hasAnyRole("ADMIN", "CUSTOMER", "REFUGEE");
http.authorizeRequests().antMatchers(HttpMethod.POST, "/tags").hasAnyRole("ADMIN", "REFUGEE");
http.authorizeRequests().antMatchers(HttpMethod.GET, "/tags/recommendation/random").hasAnyRole("ADMIN", "CUSTOMER", "REFUGEE");
http.authorizeRequests().antMatchers(HttpMethod.PUT, "/tags").hasAnyRole("ADMIN");
http.authorizeRequests().antMatchers(HttpMethod.GET, "/tags/notapproved").hasAnyRole("ADMIN");
http.authorizeRequests().antMatchers(HttpMethod.PUT, "/tags/approve/{id}").hasAnyRole("ADMIN");
}
Und die AuthenticationFilter:
public class TokenAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
private UserService userService;
public TokenAuthenticationFilter(UserService userService) {
super("/");
this.userService = userService;
}
private final String HEADER_SECURITY_TOKEN = "Authorization";
private final String PARAMETER_SECURITY_TOKEN = "access_token";
private String token = "";
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
this.token = request.getHeader(HEADER_SECURITY_TOKEN);
if ("".equals(this.token) || this.token == null) {
this.token = request.getParameter(PARAMETER_SECURITY_TOKEN);
}
//Attempt to authenticate
Authentication authResult;
authResult = attemptAuthentication(request, response);
if (authResult == null) {
chain.doFilter(request, response);
} else {
successfulAuthentication(request, response, chain, authResult);
}
}
/**
* Attempt to authenticate request - basically just pass over to another
* method to authenticate request headers
*/
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
Authentication userAuthenticationToken = authUserByToken();
if (userAuthenticationToken == null) {
//throw new AuthenticationServiceException(MessageFormat.format("Error | {0}", "Bad Token"));
}
return userAuthenticationToken;
}
/**
* authenticate the user based on token, mobile app secret & user agent
*
* @return
*/
private Authentication authUserByToken() {
Authentication securityToken = null;
try {
User user = userService.findUserByAccessToken(this.token);
securityToken = new PreAuthenticatedAuthenticationToken(
user, null, user.getAuthorities());
} catch (Exception e) {
logger.error("Authenticate user by token error: ", e);
}
return securityToken;
}
@Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain,
Authentication authResult) throws IOException, ServletException {
SecurityContextHolder.getContext().setAuthentication(authResult);
chain.doFilter(request, response);
}
Und der einfache Test:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath*:spring/test/test-servlet.xml"})
@WebAppConfiguration
public class TagControllerTest {
private MockMvc mockMvc;
private TagService tagServiceMock;
@Autowired
private FilterChainProxy springSecurityFilterChain;
@Resource
private WebApplicationContext webApplicationContext;
@Before
public void setUp() {
tagServiceMock = mock(TagService.class);
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.webApplicationContext)
.addFilters(this.springSecurityFilterChain)
.build();
}
public TagControllerTest() {
}
/**
* Test of getAllTags method, of class TagController.
*/
@Test
public void testGetAllTags() throws Exception {
List<Tags> tagsList = new ArrayList<>();
tagsList.add(new Tags((long) 1, "test", 2, 1));
tagsList.add(new Tags((long) 2, "test2", 4, 0));
User user = TestUtil.EMPTY_USER;
String query = "";
String notIncluded = "";
Integer limit = 8;
Integer start = 0;
Language language = new Language(0);
Boolean approved = false;
when(tagServiceMock.findTagsByQuery(user, query, limit, start, language, approved)).thenReturn(tagsList);
when(tagServiceMock.findTags(user, limit, start, language)).thenReturn(tagsList);
SecurityContextHolder.getContext().setAuthentication(TestUtil.getPrincipal("ROLE_ADMIN"));
mockMvc.perform(get("/tags").with(securityContext(SecurityContextHolder.getContext())).header("host", "localhost:80"))
.andExpect(status().isOk())
.andExpect(content().contentType(TestUtil.APPLICATION_JSON_UTF8))
.andExpect(jsonPath("$", hasSize(2)))
.andExpect(jsonPath("$[0].id", is(1)))
.andExpect(jsonPath("$[1].id", is(2)))
.andExpect(jsonPath("$[0].label", is("test")))
.andExpect(jsonPath("$[1].label", is("test2")))
.andExpect(jsonPath("$[0].count", is(2)))
.andExpect(jsonPath("$[1].count", is(4)))
.andExpect(jsonPath("$[0].approved", is(1)))
.andExpect(jsonPath("$[1].approved", is(0)));
verify(tagServiceMock, times(1)).findTagsByQuery(user, query, limit, start, language, approved);
verifyNoMoreInteractions(tagServiceMock);
}
Das Problem ist, dass ich immer einen 403 -> Zugriff verweigert. Es ist wegen des TokenAuthenticationFilter. Wenn ich eine Kopfzeile mit dem Namen "Authorization" mit einem accesstoken setze, das korrekt ist (korrekt bedeutet, dass es von einem tatsächlichen Benutzer verwendet wird), dann funktioniert es. Aber ich denke, das ist nicht so, wie es für Komponententests sein sollte. Also was zu tun? Wie setze ich eine Rolle und übergebe den SecurityFilter?