【发布时间】:2017-10-21 09:24:56
【问题描述】:
我有一个简单的应用程序,我使用自定义 MySql 数据库设置了 Spring Security。现在我正在为它编写测试用例,它们似乎在登录页面和登录后任何有效的东西上都失败了。我的问题是如何为它编写测试用例来检查成功登录和后续请求?
我的安全配置:
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private BCryptPasswordEncoder bCryptPasswordEncoder;
@Autowired
private DataSource dataSource;
@Value("${spring.queries.users-query}")
private String usersQuery;
@Value("${spring.queries.roles-query}")
private String rolesQuery;
@Autowired
private CustomAuthenticationSuccessHandler successHandler;
/** Providing the queries and data source for security*/
@Override
protected void configure(AuthenticationManagerBuilder auth)
throws Exception
{
auth.
jdbcAuthentication()
.usersByUsernameQuery(usersQuery)
.authoritiesByUsernameQuery(rolesQuery)
.dataSource(dataSource)
.passwordEncoder(bCryptPasswordEncoder);
}
/** Defining fine grained access for ADMIN and CUSTOMER user */
@Override
protected void configure(HttpSecurity http) throws Exception {
http.
authorizeRequests()
.antMatchers("/").permitAll()
.antMatchers("/login").permitAll()
.antMatchers("/registration").permitAll()
.antMatchers("/user/**").hasAuthority(AppRole.CUSTOMER.toString())
.antMatchers("/health/**").hasAuthority(AppRole.ADMIN.toString())
.antMatchers("/admin/**").hasAuthority(AppRole.ADMIN.toString()).anyRequest()
.authenticated().and().csrf().disable().formLogin()
.loginPage("/login").failureUrl("/login?error=true")
.successHandler(successHandler)
.usernameParameter("username")
.passwordParameter("password")
.and().logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.logoutSuccessUrl("/").and().exceptionHandling()
.accessDeniedPage("/access-denied");
}
/** Defining ant matchers that should ignore the paths and provide no access to any one */
@Override
public void configure(WebSecurity web) throws Exception
{
web
.ignoring()
.antMatchers("/resources/**", "/static/**", "/css/**", "/js/**", "/images/**");
}
}
我的自定义成功处理程序:
@Component
@Configuration
public class CustomAuthenticationSuccessHandler implements AuthenticationSuccessHandler
{
/** Getting reference to UserService */
@Autowired
private UserService userService;
@Override
public void onAuthenticationSuccess(HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse, Authentication authentication)
throws IOException, ServletException, RuntimeException
{
HttpSession session = httpServletRequest.getSession();
User authUser = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
com.crossover.techtrial.java.se.model.User user = userService.findUserByUsername(authUser.getUsername());
session.setAttribute("userId", user.getUserId());
session.setAttribute("username", authUser.getUsername());
session.setAttribute("accountId", user.getAccountId());
//set our response to OK status
httpServletResponse.setStatus(HttpServletResponse.SC_OK);
Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
authorities.forEach(authority ->
{
if(authority.getAuthority().equals(AppRole.ADMIN.toString()))
{
session.setAttribute("role", AppRole.ADMIN);
try
{
//since we have created our custom success handler, its up to us to where
//we will redirect the user after successfully login
httpServletResponse.sendRedirect("/admin/home");
}
catch (IOException e)
{
throw new RuntimeException(e);
}
}
else if (authority.getAuthority().equals(AppRole.CUSTOMER.toString()))
{
session.setAttribute("role", AppRole.CUSTOMER);
try
{
//since we have created our custom success handler, its up to us to where
//we will redirect the user after successfully login
httpServletResponse.sendRedirect("/user/home");
}
catch (IOException e)
{
throw new RuntimeException(e);
}
}
});
}
}
经过一番搜索后,我尝试编写这样的测试用例,但它们似乎不起作用:
@RunWith(SpringRunner.class)
@SpringBootTest
public class TrialApplicationTests
{
@Autowired
private WebApplicationContext webApplicationContext;
@Autowired
private FilterChainProxy springSecurityFilterChain;
@Autowired
private MockHttpServletRequest request;
private MockMvc mockMvc;
@Test
public void contextLoads()
{
}
@Before
public void setup()
{
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext)
.addFilters(springSecurityFilterChain)
.build();
}
@Test
public void verifiesLoginPageLoads() throws Exception
{
mockMvc.perform(MockMvcRequestBuilders.get("/"))
.andExpect(MockMvcResultMatchers.model().hasNoErrors())
.andExpect(MockMvcResultMatchers.view().name("login"))
.andExpect(MockMvcResultMatchers.status().isOk());
}
@Test
public void testUserLogin() throws Exception
{
HttpSession session = mockMvc.perform(post("/login")
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.param("username", "test")
.param("password", "test123")
)
.andExpect(MockMvcResultMatchers.status().isOk())
//.andExpect(redirectedUrl("/user/home"))
.andReturn()
.getRequest()
.getSession();
request.setSession(session);
SecurityContext securityContext = (SecurityContext) session.getAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY);
SecurityContextHolder.setContext(securityContext);
}
@Test
public void testRetrieveUserBookings() throws Exception
{
testUserLogin();
mockMvc.perform(MockMvcRequestBuilders.get("user/bookings"))
.andExpect(MockMvcResultMatchers.model().hasNoErrors())
.andExpect(MockMvcResultMatchers.model().attributeExists("bookings"))
.andExpect(MockMvcResultMatchers.view().name("user/bookings"))
.andExpect(content().string(containsString("Booking")));
}
}
I searched on the net and there are links WithMockUser and UserDetails, but the problem is as you can see I'm setting a my primary key userId in the session in my custom success handler. So I would also need to get the session in my test. Please tell me the simplest way to write tests that will work, possibly with code since I'm new with security and all such.
UPDATE:
I changed the code as suggested but still getting the 404 error on my testRetrieveUserBookings. Any more ideas?
@RunWith(SpringRunner.class)
@ContextConfiguration
@SpringBootTest
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@TestExecutionListeners(listeners={ServletTestExecutionListener.class,
DependencyInjectionTestExecutionListener.class,
DirtiesContextTestExecutionListener.class,
TransactionalTestExecutionListener.class,
WithSecurityContextTestExecutionListener.class})
public class TrialApplicationTests
{
@Autowired
private WebApplicationContext webApplicationContext;
MockMvc mockMvc;
@Autowired
ForestApiClient apiClient;
@Autowired
AccountClient accountClient;
@Autowired
AirlineClient airlineClient;
@Autowired
UserService userService;
private final String INTEGRATION_ACCOUNT = "account1";
private MockHttpSession mockSession;
private Authentication authentication;
@Test
public void contextLoads()
{
}
@Before
public void setup()
{
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext)
//.addFilters(springSecurityFilterChain)
.build();
mockSession = new MockHttpSession(webApplicationContext.getServletContext(), UUID.randomUUID().toString());
mockSession.setAttribute("userId", 3);
mockSession.setAttribute("accountId", "ZWR26539");
}
@Test
public void testVerifiesLoginPageLoads() throws Exception
{
mockMvc.perform(MockMvcRequestBuilders.get("/"))
.andExpect(MockMvcResultMatchers.model().hasNoErrors())
.andExpect(MockMvcResultMatchers.view().name("login"))
.andExpect(MockMvcResultMatchers.status().isOk());
}
@Test
public void testRegistration() throws Exception
{
mockMvc.perform(post("/registration")
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.param("username", "test2")
.param("password", "test123")
.param("email", "crossovertestuser@gmail.com")
.param("address", "Some Address")
.param("accountCurrency", "USD")
)
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(MockMvcResultMatchers.model().hasNoErrors())
.andExpect(MockMvcResultMatchers.model().attributeExists("user"))
.andExpect(MockMvcResultMatchers.view().name("registration"))
.andExpect(content().string(containsString("User has been registered successfully")));
}
@Test
@WithMockUser(username="test",roles={"USER","ADMIN"})
public void testRetrieveUserBookings() throws Exception
{
mockMvc.perform(MockMvcRequestBuilders.get("user/bookings"))
.andExpect(MockMvcResultMatchers.model().hasNoErrors())
.andExpect(MockMvcResultMatchers.model().attributeExists("bookings"))
.andExpect(MockMvcResultMatchers.view().name("user/bookings"))
.andExpect(content().string(containsString("Booking")));
}
}
【问题讨论】:
标签: spring-boot junit spring-security spring-test mockmvc