Spring Test & Security:如何模拟身份验证?

我试图弄清楚如何对我的控制器的URL进行适当安全的单元测试。以防万一有人更改周围的内容并意外删除安全设置。

我的控制器方法如下所示:

@RequestMapping("/api/v1/resource/test") 

@Secured("ROLE_USER")

public @ResonseBody String test() {

return "test";

}

我像这样设置一个WebTestEnvironment:

import javax.annotation.Resource;

import javax.naming.NamingException;

import javax.sql.DataSource;

import org.junit.Before;

import org.junit.runner.RunWith;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.beans.factory.annotation.Qualifier;

import org.springframework.context.support.ClassPathXmlApplicationContext;

import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;

import org.springframework.security.core.userdetails.UserDetails;

import org.springframework.security.core.userdetails.UserDetailsService;

import org.springframework.security.web.FilterChainProxy;

import org.springframework.test.context.ActiveProfiles;

import org.springframework.test.context.ContextConfiguration;

import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import org.springframework.test.context.web.WebAppConfiguration;

import org.springframework.test.web.servlet.MockMvc;

import org.springframework.test.web.servlet.setup.MockMvcBuilders;

import org.springframework.web.context.WebApplicationContext;

@RunWith(SpringJUnit4ClassRunner.class)

@WebAppConfiguration

@ContextConfiguration({

"file:src/main/webapp/WEB-INF/spring/security.xml",

"file:src/main/webapp/WEB-INF/spring/applicationContext.xml",

"file:src/main/webapp/WEB-INF/spring/servlet-context.xml" })

public class WebappTestEnvironment2 {

@Resource

private FilterChainProxy springSecurityFilterChain;

@Autowired

@Qualifier("databaseUserService")

protected UserDetailsService userDetailsService;

@Autowired

private WebApplicationContext wac;

@Autowired

protected DataSource dataSource;

protected MockMvc mockMvc;

protected final Logger logger = LoggerFactory.getLogger(this.getClass());

protected UsernamePasswordAuthenticationToken getPrincipal(String username) {

UserDetails user = this.userDetailsService.loadUserByUsername(username);

UsernamePasswordAuthenticationToken authentication =

new UsernamePasswordAuthenticationToken(

user,

user.getPassword(),

user.getAuthorities());

return authentication;

}

@Before

public void setupMockMvc() throws NamingException {

// setup mock MVC

this.mockMvc = MockMvcBuilders

.webAppContextSetup(this.wac)

.addFilters(this.springSecurityFilterChain)

.build();

}

}

在我的实际测试中,我尝试执行以下操作:

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;

import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

import org.junit.Test;

import org.springframework.mock.web.MockHttpSession;

import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;

import org.springframework.security.core.context.SecurityContextHolder;

import org.springframework.security.web.context.HttpSessionSecurityContextRepository;

import eu.ubicon.webapp.test.WebappTestEnvironment;

public class CopyOfClaimTest extends WebappTestEnvironment {

@Test

public void signedIn() throws Exception {

UsernamePasswordAuthenticationToken principal =

this.getPrincipal("test1");

SecurityContextHolder.getContext().setAuthentication(principal);

super.mockMvc

.perform(

get("/api/v1/resource/test")

// .principal(principal)

.session(session))

.andExpect(status().isOk());

}

}

JUnit如何测试@PreAuthorize批注及其由spring MVC控制器指定的spring EL?

但是,如果仔细观察,这仅在不向URL发送实际请求时才有帮助,而仅在功能级别上测试服务时才有帮助。在我的情况下,抛出了“拒绝访问”异常:

org.springframework.security.access.AccessDeniedException: Access is denied

at org.springframework.security.access.vote.AffirmativeBased.decide(AffirmativeBased.java:83) ~[spring-security-core-3.1.3.RELEASE.jar:3.1.3.RELEASE]

at org.springframework.security.access.intercept.AbstractSecurityInterceptor.beforeInvocation(AbstractSecurityInterceptor.java:206) ~[spring-security-core-3.1.3.RELEASE.jar:3.1.3.RELEASE]

at org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor.invoke(MethodSecurityInterceptor.java:60) ~[spring-security-core-3.1.3.RELEASE.jar:3.1.3.RELEASE]

at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172) ~[spring-aop-3.2.1.RELEASE.jar:3.2.1.RELEASE]

...

以下两个日志消息是值得注意的,它们基本上表明没有用户经过身份验证,表明该设置Principal无效或已被覆盖。

14:20:34.454 [main] DEBUG o.s.s.a.i.a.MethodSecurityInterceptor - Secure object: ReflectiveMethodInvocation: public java.util.List test.TestController.test(); target is of class [test.TestController]; Attributes: [ROLE_USER]

14:20:34.454 [main] DEBUG o.s.s.a.i.a.MethodSecurityInterceptor - Previously Authenticated: org.springframework.security.authentication.AnonymousAuthenticationToken@9055e4a6: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@957e: RemoteIpAddress: 127.0.0.1; SessionId: null; Granted Authorities: ROLE_ANONYMOUS

回答:

事实证明,作为SecurityContextPersistenceFilterSpring Security过滤器链的一部分的,始终会重置my SecurityContext,而我会设置调用SecurityContextHolder.getContext().setAuthentication(principal)(或使用.principal(principal)方法)。该过滤器设置SecurityContextSecurityContextHolder同一个SecurityContextSecurityContextRepository覆盖一个我先前设置。HttpSessionSecurityContextRepository默认情况下,该存储库为。被HttpSessionSecurityContextRepository检查的给定HttpRequest并尝试访问相应的HttpSession。如果它存在,它会尝试读取SecurityContext距离HttpSession。如果失败,则存储库将生成一个empty SecurityContext

因此,我的解决方案是将HttpSession连同请求一起传递,其中包含SecurityContext

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;

import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

import org.junit.Test;

import org.springframework.mock.web.MockHttpSession;

import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;

import org.springframework.security.core.context.SecurityContextHolder;

import org.springframework.security.web.context.HttpSessionSecurityContextRepository;

import eu.ubicon.webapp.test.WebappTestEnvironment;

public class Test extends WebappTestEnvironment {

public static class MockSecurityContext implements SecurityContext {

private static final long serialVersionUID = -1386535243513362694L;

private Authentication authentication;

public MockSecurityContext(Authentication authentication) {

this.authentication = authentication;

}

@Override

public Authentication getAuthentication() {

return this.authentication;

}

@Override

public void setAuthentication(Authentication authentication) {

this.authentication = authentication;

}

}

@Test

public void signedIn() throws Exception {

以上是 Spring Test & Security:如何模拟身份验证? 的全部内容, 来源链接: utcz.com/qa/428873.html

回到顶部