2015-03-31 384 views
0

我使用Spring 4.1.5和Spring Security 4.0.0.RELEASE。如何在Spring Security测试中通过WithSecurityContextFactory设置SecurityContext?

我读http://spring.io/blog/2014/05/07/preview-spring-security-test-method-security(罗布绞盘很好的文章),并开发了我自己的实现WithSecurityContextFactory,才能够测试我的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; 
    } 
} 

我的抽象资源测试类是如下:

@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" }) 
    @TestExecutionListeners(listeners= 
    { 
    ServletTestExecutionListener.class, 
    DependencyInjectionTestExecutionListener.class, 
    DirtiesContextTestExecutionListener.class, 
    TransactionalTestExecutionListener.class, 
    WithSecurityContextTestExcecutionListener.class }) 

    public abstract class AbstractResourceMockMvcTest { 

    @Autowired 
    private WebApplicationContext wac; 

    @Autowired 
    private Filter springSecurityFilterChain; 

    private MockMvc mockMvc; 

    [...] 

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

    [...] 

} 

然后,我的具体测试类继承自AbstractResourceTest(从上面),它在@ Test-enabled方法上使用以下注释:

@WithMockCustomUser(faps={"promotion_read"}) 

跟踪代码,我可以确认WithMockCustomUserSecurityContextFactory.createSecurityContext()被调用,并且它的返回值在SecurityContextHolder.setContext()中设置(通过TestSecurityContextHolder.setContext())。

到目前为止,太棒了!然后,在后面的过程中,SecurityContextPersistenceFilter.doFilter()调用SecurityContextHolder.setContext(),这会覆盖测试设置的上下文,并且失去了我准备的模拟安全上下文的跟踪。

security.xml文件:

<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> 

servlet.xml中:

<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> 

我怎样才能防止这种安全上下文覆盖?我的security.xml中是否包含我错过的明显缺陷?

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

在此先感谢!

回答

2

不幸的是,博客文章只是为了方法级别的安全性,并没有完整的MockMvc设置说明(本系列中的以下博客)。此外,博客实际上是过时的(我更新了它们以反映读者应该参考参考文档)。您可以在参考文献的Testing Section中找到更新的说明。

总之,更新您的代码如下:

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
  • 使用。适用(springSecurity())而不是手动添加弹簧安全过滤器链条

此作品因为Spring Security的测试支持,即apply(springSecurity())将覆盖springSecurityFilterChain使用的SecurityContextRepository,以首先尝试TestSecurityContextHolder。

+0

springSecurity()帮助我获得200我曾经有401 – BigDong 2017-11-18 17:05:44

相关问题