Spring Selenium 测试的安全@WithMockUser

Spring Security @WithMockUser for Selenium test

我刚刚发现了新的 Spring Security 4 测试注释 @WithMockUser,但我无法让它用于我的 Selenium 测试。 问题是,这个注解创建了一个模拟 SecurityContext,但是 HttpSessionSecurityContextRepository 创建了一个新的,因为 Selenium 运行基于真实浏览器的测试。 我能以某种方式告诉 Spring 使用模拟 SecurityContext 作为下一个会话安全上下文吗?

谢谢!

我找到了使用测试类路径中的新过滤器对模拟用户进行身份验证的方法:

@Component
public class MockUserFilter extends GenericFilterBean {

    @Autowired
    private UserDetailsService userDetailsService;

    private SecurityContext securityContext;

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        if (securityContext != null) {
            SecurityContextRepository securityContextRepository = WebTestUtils.getSecurityContextRepository(request);

            HttpRequestResponseHolder requestResponseHolder = new HttpRequestResponseHolder(request, response);
            securityContextRepository.loadContext(requestResponseHolder);

            request = requestResponseHolder.getRequest();
            response = requestResponseHolder.getResponse();

            securityContextRepository.saveContext(securityContext, request, response);

            securityContext = null;
        }
        chain.doFilter(request, response);
    }

    public void authenticateNextRequestAs(String username) {
        UserDetails principal = userDetailsService.loadUserByUsername(username);
        Authentication authentication = new UsernamePasswordAuthenticationToken(principal, principal.getPassword(), principal.getAuthorities());
        securityContext = SecurityContextHolder.createEmptyContext();
        securityContext.setAuthentication(authentication);
    }
}

它的灵感来自 SecurityMockMvcRequestPostProcessorsWithUserDetailsSecurityContextFactory

我无法使用 @WithUserDetails 注释,因为我 运行 Cucumber 测试,但是使用这个过滤器我可以在一行中为下一个请求模拟身份验证:testSecurityFilter.authenticateNextRequestAs("username");

我添加这个答案是因为虽然接受的答案帮助我形成了一个解决方案,但我必须做出一些改变才能让它发挥作用。这个答案也帮助我让它工作:

这是我的 MockUserFilter:

        @Component("MockUserFilter")
        public class MockUserFilter extends GenericFilterBean {
            @Autowired
            private UserDetailService userDetailService;

            private SecurityContext securityContext;

            @Autowired
            private AuthenticationProvider authenticationProvider;

            public void setUserDetailService(UserDetailService userDetailService) {
                this.userDetailService = userDetailService;
            }
            @Override
            public void doFilter(ServletRequest request, ServletResponse response,
                    FilterChain chain) throws IOException, ServletException {
                HttpServletRequest servletRequest = (HttpServletRequest) request;
                HttpServletResponse servletResponse = (HttpServletResponse) response;

                if (securityContext != null) {
                    SecurityContextRepository securityContextRepository = WebTestUtils.getSecurityContextRepository(servletRequest);
                    HttpRequestResponseHolder requestResponseHolder = new HttpRequestResponseHolder(servletRequest, servletResponse);
                    securityContextRepository.loadContext(requestResponseHolder);
                    servletRequest = requestResponseHolder.getRequest();
                    servletResponse = requestResponseHolder.getResponse();
                    securityContextRepository.saveContext(securityContext, servletRequest, servletResponse);
                    securityContext = null;
                }

                chain.doFilter(request, response);
            }

             public void authenticateNextRequestAs(String username, ServletRequest request) {
                 UserDetails principal = userDetailService.loadUserByUsername(username);
                 Authentication authentication = new UsernamePasswordAuthenticationToken(principal, principal.getPassword(), principal.getAuthorities());
                 securityContext = SecurityContextHolder.createEmptyContext();
                 securityContext.setAuthentication(authentication);
                 SecurityContextHolder.getContext().setAuthentication(authentication);

                 HttpSession session = ((HttpServletRequest) request).getSession(true);
                 session.setAttribute("SPRING_SECURITY_CONTEXT", securityContext);
            }
        }

除此之外,我还必须从过滤器链中删除我的 casAuthenticationFilter 才能使其正常工作。我使用属性值 enable/disable 这个。

我对 Spring 和 Spring 安全性比较陌生,因此欢迎对此解决方案提出任何意见。我不确定 "good" 或 "bad" 这个解决方案如何。

要记住的一件事是,这是一种用于本地测试或在安全环境中进行测试的解决方案,而不是您希望在开发环境中使用的解决方案。