如何在 Spring MVC 中模拟安全上下文以进行测试

Posted

技术标签:

【中文标题】如何在 Spring MVC 中模拟安全上下文以进行测试【英文标题】:How to Mock the security context in Spring MVC for testing 【发布时间】:2013-08-18 17:19:30 【问题描述】:

我需要测试一些受保护的 url,因此我需要在我的测试 (junit) 中设置一个模拟安全上下文。 特别是我需要使用经过身份验证的用户对我的 Web 应用程序执行一些获取和发布。 下面是我的代码,我可以创建这样的安全上下文,但我需要将它注入到“MockMvc”对象中。 我在安全上下文中设置了身份验证对象并且它可以工作,'SecurityContextHolder.getContext().getAuthentication().getPrincipal()' 的输出结果是 chanelle.evans@616747.com 但是当我在 /profile 上调用 GET 时,我有一个断言错误,因为我被重定向到我的登录页面,而不是 /profile。

@WebAppConfiguration
@ContextConfiguration(locations = "classpath:spring/security.xml", "classpath:spring/view.xml")
@ActiveProfiles("default")
@RunWith(SpringJUnit4ClassRunner.class)
public class AuthenticationTest 

@Autowired
WebApplicationContext ctx;

private MockMvc mockMvc;

@Autowired
private FilterChainProxy springSecurityFilterChain;

@BeforeClass
public static void setUpBeforeClass() throws Exception 


@AfterClass
public static void tearDownAfterClass() throws Exception 


@Before
public void setUp() throws Exception 
    mockMvc = MockMvcBuilders.webAppContextSetup(ctx).addFilters(springSecurityFilterChain).build();

    //@formatter:off

    UserDetailsLogic userDetailsLogic = null;
    userDetailsLogic = ctx.getBean(UserDetailsLogic.class);
    final UserDetailsImp userDetailsImp = new UserDetailsImp();
    userDetailsImp.setAccountId(1001);
    userDetailsImp.setUserId(8001);
    userDetailsImp.setPassword("a378c92df7531df6fdf351f7ae1713f91f2dd2d45b9c6e1a8b02736ee3afec6595ff60465e9cb8da");
    userDetailsImp.setUsername("chanelle.evans@616747.com");
    userDetailsImp.setEmail("chanelle.evans@616747.com");

    final Collection<GrantedAuthorityImplementation> authorities= new ArrayList<GrantedAuthorityImplementation>();
    authorities.add(new GrantedAuthorityImplementation("ROLE_USER"));

    userDetailsImp.setAuthorities(authorities);

    userDetailsImp.setAccountNonExpired(true);
    userDetailsImp.setAccountNonLocked(true);
    userDetailsImp.setCredentialsNonExpired(true);
    userDetailsImp.setEnabled(true);

    final Authentication authToken = new UsernamePasswordAuthenticationToken (userDetailsImp.getUsername(), userDetailsImp.getPassword(), userDetailsImp.getAuthorities());
    SecurityContextHolder.getContext().setAuthentication(authToken);

    System.out.println("principal:"+SecurityContextHolder.getContext().getAuthentication().getPrincipal());      
    mockMvc.perform(get("/profile").principal(authToken)

            .contentType(MediaType.TEXT_html)

            .accept(MediaType.TEXT_HTML))

            .andDo(print())

            .andExpect(status().isOk())
            .andExpect(redirectedUrl(null))
            .andExpect(forwardedUrl(null));
    //@formatter:on     

我想我应该将我的身份验证对象放在 MockMvc 对象中,但我不知道如何 有人知道吗?

【问题讨论】:

【参考方案1】:

这是我几天前写的。我认为这可能会有所帮助(我针对登录表单测试了相同的内容,使用会话进行第二个请求)请参阅 loginUser1Ok(..))

见MvcTest.java

m4nuv/easy-bank.

【讨论】:

嗨,Emanuele,感谢您的回答,我最终得到了完全相同的解决方案。 您提供的代码示例对我非常有用!谢谢! 登录以获取会话,然后进行端点调用的方式很好。我希望创建一个模拟会话而不必执行登录 - 然后欺骗凭据。登录是由与我正在测试的服务不同的服务执行的,所以这对我不起作用。 嗨,您应该能够创建一个模拟会话并用您需要的所有内容填充它,然后在执行请求时将其设置为上下文。【参考方案2】:

在 pom.xml 中添加

<repository>
    <id>spring-snaspho</id>
    <url>http://repo.springsource.org/libs-milestone/</url>
</repository>

<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-test</artifactId>
    <version>4.0.0.M1</version>
</dependency>

并使用org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors 进行授权请求。 See the sample usage here 和 Spring.io jira ticket SEC-2592。

【讨论】:

Spring Security 在当前版本 (4.0.1.RELEASE) 中改进了测试支持。它的文档可用于设置 MockMvc 和 Spring Security(docs.spring.io/spring-security/site/docs/4.0.1.RELEASE/…) 并使用 Mocked user (docs.spring.io/spring-security/site/docs/4.0.1.RELEASE/…)

以上是关于如何在 Spring MVC 中模拟安全上下文以进行测试的主要内容,如果未能解决你的问题,请参考以下文章

Spring Boot:使用 Junit 模拟具有安全上下文的用户身份验证

如何在 Spring.Net 应用程序上下文中添加 ASP.NET MVC 控制器?

如何在 Spring MVC 应用程序中实现 Spring 安全性?

Spring MVC + Thymeleaf:将变量添加到所有模板的上下文中

在Spring MVC中使用Apache Shiro安全框架

Spring MVC集成测试弹簧安全问题,怎么解决