2015-03-31 27 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

相關問題