Spring security 如何在Spring安全测试中通过SecurityContextFactory设置SecurityContext?

Spring security 如何在Spring安全测试中通过SecurityContextFactory设置SecurityContext?,spring-security,Spring Security,我使用的是Spring4.1.5和SpringSecurity4.0.0.RELEASE 我阅读了Rob Winch的文章,并开发了自己的WithSecurity ContextFactory实现,以便能够测试我的Spring MVC控制器: public class WithMockCustomUserSecurityContextFactory implements WithSecurityContextFactory<WithMockCustomUser> { @Ov

我使用的是Spring4.1.5和SpringSecurity4.0.0.RELEASE

我阅读了Rob Winch的文章,并开发了自己的WithSecurity ContextFactory实现,以便能够测试我的Spring MVC控制器:

public class WithMockCustomUserSecurityContextFactory implements WithSecurityContextFactory<WithMockCustomUser> {

    @Override
    public SecurityContext createSecurityContext(WithMockCustomUser customUser) {
        final User fakeUser = new User();
        final SecurityUser principal = new SecurityUser(fakeUser);
        final Authentication auth = new UsernamePasswordAuthenticationToken(principal, "password", HelpersTest.getAuthorities(customUser.faps()));

        final SecurityContext context = SecurityContextHolder.createEmptyContext();
        context.setAuthentication(auth);

        return context;
    }
}
然后,我的具体测试类继承自AbstractResourceTest(从上面),并在启用@test的方法上使用以下注释:

@WithMockCustomUser(faps={"promotion_read"})
跟踪代码,我可以使用MockCustomUserSecurityContextFactory进行确认。调用createSecurityContext(),并在SecurityContextHolder.setContext()中设置其返回值(通过TestSecurityContextHolder.setContext())

到目前为止,一切都很好

但是,在该过程的稍后部分,SecurityContextPersistenceFilter.doFilter()调用SecurityContextHolder.setContext(),这将覆盖测试设置的上下文,我将无法跟踪我准备的模拟安全上下文

security.xml:


servlet.xml:


如何防止此安全上下文覆盖?我的security.xml是否包含我遗漏的明显缺陷

PS:我跳过了其他上下文配置文件,因为它们似乎与问题无关


提前谢谢

不幸的是,这篇博文只是为了方法级安全,没有关于MockMvc设置的完整说明(本系列中的以下博文有)。此外,这些博客实际上是过时的(我已经对它们进行了更新,以反映读者应该参考参考文档)。您可以在参考的中找到更新的说明

简而言之,将代码更新为以下内容:

import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.*;

@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration(locations =
{
"classpath:spring/mock-daos-and-scan-for-services.xml",
"classpath:security.xml",
"classpath:singletons.xml",
"classpath:controller-scan.xml",
"classpath:servlet.xml" })
public abstract class AbstractResourceMockMvcTest {

    @Autowired
    private WebApplicationContext wac;

    private MockMvc mockMvc;

    [...]

    @Before
    public void setup() {
        this.mockMvc = 
            MockMvcBuilders.webAppContextSetup(this.getWac())
            .apply(springSecurity()) 
            .build();
    }

    @Test
    @WithMockCustomUser(faps={"promotion_read"})
    public void myTest() {
        ...
    }

    [...]

}
一些亮点:

  • 您不再需要提供TestExecutionListeners
  • 使用.apply(springSecurity())而不是手动添加springSecurity筛选器链
这是因为Spring Security的测试支持,即
apply(springSecurity())
将覆盖springSecurityFilterChain使用的SecurityContextRepository,以首先尝试TestSecurityContextHolder。

springSecurity()帮助我获得了200,而我以前只有401
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:security="http://www.springframework.org/schema/security"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans    http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
        http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-4.0.xsd
    "
>

    <!-- HTTP security handling -->
    <security:http use-expressions="true">

        <security:logout logout-url="/j_spring_security_logout" invalidate-session="true" logout-success-url="/login.jsp?loggedout=true" />

        <security:custom-filter before="FIRST" ref="multiTenantRequestFilter" />

        <!-- make sure following page are not secured -->

        <security:intercept-url pattern="/*/*/internal/**" access="hasIpAddress('127.0.0.1')" />

        <!-- make sure everything else going through the security filter is secured -->

        <security:intercept-url pattern="/resources/**" access="hasRole('ROLE_USER')" requires-channel="any" />

        <!-- supporting basic authentication for unattended connections (web services) -->

        <security:http-basic />

    </security:http>

    <!-- authentication strategy -->

    <security:authentication-manager alias="authManager">
        <security:authentication-provider user-service-ref="userSecurityService">
            <security:password-encoder ref="passwordEncoder" />
        </security:authentication-provider>
    </security:authentication-manager>

    <!-- custom filter to intercept the tenant name from the login form -->

    <bean id="multiTenantRequestFilter" class="com.meicpg.ti.web.MultiTenantRequestFilter" />

</beans>
<beans
    xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:security="http://www.springframework.org/schema/security"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:task="http://www.springframework.org/schema/task"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans   http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd
        http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-4.0.xsd
        http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd
        http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-4.1.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.1.xsd
    "
>
    <mvc:annotation-driven>
        <!-- Content skipped for StackOverflow question -->
    </mvc:annotation-driven>

    <context:annotation-config />

    <bean id="annotationExceptionResolver" class="org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver"></bean>

    <security:global-method-security pre-post-annotations="enabled"/>

    <aop:aspectj-autoproxy proxy-target-class="true"/>
</beans>
import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.*;

@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration(locations =
{
"classpath:spring/mock-daos-and-scan-for-services.xml",
"classpath:security.xml",
"classpath:singletons.xml",
"classpath:controller-scan.xml",
"classpath:servlet.xml" })
public abstract class AbstractResourceMockMvcTest {

    @Autowired
    private WebApplicationContext wac;

    private MockMvc mockMvc;

    [...]

    @Before
    public void setup() {
        this.mockMvc = 
            MockMvcBuilders.webAppContextSetup(this.getWac())
            .apply(springSecurity()) 
            .build();
    }

    @Test
    @WithMockCustomUser(faps={"promotion_read"})
    public void myTest() {
        ...
    }

    [...]

}