如何使用Spring Security对Spring Boot App进行单元测试

时间:2017-08-19 07:15:19

标签: spring-boot junit spring-security mockmvc

我有一个简单的应用程序,我使用自定义MySql数据库设置了spring安全性。您可以在github上查看完整的应用。现在的问题是我正在为它编写测试用例,它们似乎在登录页面上失败,并且在登录后有效。我的问题是如何编写测试用例来检查成功登录和后续请求?

我的安全配置:

@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")));
        }

    }

我在网上搜索,并且有一些链接WithMockUser和UserDetails,但问题是你可以看到我在我的自定义成功处理程序中的会话中设置了我的主键userId。所以我还需要在我的测试中获得会话。请告诉我编写可行的测试的最简单方法,可能使用代码,因为我是安全的新手,所有这些。

我有一段时间没遇到这个问题,但我找不到任何解决方案。现在我又来了。

0 个答案:

没有答案