Spring 控制器上的 PreAuthorize 注释在测试时被忽略
PreAuthorize annotations on Spring controllers are ignored when testing
我正在使用 JUnit、Mockito 和 spring-test 测试我的 Spring MVC 控制器。不幸的是,测试忽略了我的控制器方法上的 @PreAuthorize 注释,我一直无法解决这个问题。关键代码片段如下,尽管我已经从 MyController 的依赖项中删除了不相关的模拟响应逻辑以保持简短。我正在使用 Spring 3.2 和 JUnit 4,并且我 运行 通过 Maven 和直接通过 Eclipse 进行测试(运行 作为 -> JUnit 测试)。
现在我期待 getAccounts_ReturnsOkStatus 测试失败,因为我没有提供任何身份验证和 /accounts 路由映射到的方法被注释预授权注释,但是正在调用该方法并绕过预授权检查。
MyControllerTest.java
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:applicationContext-test.xml" })
public class MyControllerTest {
private MockMvc mockMvc;
@Mock
private MyService myService;
@InjectMocks
private MyController myController;
@Before
public void init() {
MockitoAnnotations.initMocks(this);
mockMvc = MockMvcBuilders.standaloneSetup(myController).build();
}
@Test
public void getAccounts_ReturnsOkStatus() throws Exception {
// Make the GET request, and verify that it returns HttpStatus.OK (200)
mockMvc.perform(MockMvcRequestBuilders.get("/accounts"))
.andExpect(MockMvcResultMatchers.status().isOk());
}
}
applicationContext-test.xml
<sec:global-method-security pre-post-annotations="enabled" />
<bean id="applicationContextProvider" class="com.myapp.springsupport.ApplicationContextProvider" />
<sec:authentication-manager alias="authenticationManager">
<sec:authentication-provider>
<sec:user-service>
<sec:user name="test-superuser" password="test-pwd" authorities="ROLE_SUPERUSER" />
</sec:user-service>
</sec:authentication-provider>
</sec:authentication-manager>
MyController.java
@PreAuthorize("isAuthenticated() and hasRole('ROLE_SUPERUSER')")
@RequestMapping(value = "/accounts", method = RequestMethod.GET)
@ResponseBody
public Collection<Account> getAccounts() {
return new ArrayList<Account>();
}
肯定使用了 applicationContext-test 应用程序上下文,因为使用
手动验证
Authentication auth = new UsernamePasswordAuthenticationToken(name, password);
SecurityContextHolder.getContext().setAuthentication(am.authenticate(auth));
仅适用于测试配置中指定的用户凭据(在没有任何其他身份验证提供程序的情况下)。此外,我可以确定预授权被忽略了,因为我已经测试过使用 SEL 调用方法并进行了调试。
我错过了什么?
您必须在构建 mockMvc
时启用 spring 安全性以进行测试。
在Spring4中是这样的:
mockMvc = MockMvcBuilders.webAppContextSetup(context)
.apply(springSecurity())
.build();
在Spring3中是这样的:
@Autowired
private Filter springSecurityFilterChain;
...
mockMvc = MockMvcBuilders.webAppContextSetup(context)
.addFilters(springSecurityFilterChain)
.build();
有关详细信息,请参阅:
- https://spring.io/blog/2014/05/23/preview-spring-security-test-web-security
- How do I unit test spring security @PreAuthorize(hasRole)?
- Spring MVC integration tests with Spring Security
MockMvcBuilders.standaloneSetup
获得手动实例化的 MyController
(没有 Spring,因此没有 AOP)。因此 @PreAuthorize
不会被截取并且跳过安全检查。因此,您可以 @Autowire 您的控制器并将其传递给 MockMvcBuilders.standaloneSetup
以 Mock MyService
使用 @MockBean
以便服务的每个实例都被 Mock 替换。
我正在使用 JUnit、Mockito 和 spring-test 测试我的 Spring MVC 控制器。不幸的是,测试忽略了我的控制器方法上的 @PreAuthorize 注释,我一直无法解决这个问题。关键代码片段如下,尽管我已经从 MyController 的依赖项中删除了不相关的模拟响应逻辑以保持简短。我正在使用 Spring 3.2 和 JUnit 4,并且我 运行 通过 Maven 和直接通过 Eclipse 进行测试(运行 作为 -> JUnit 测试)。
现在我期待 getAccounts_ReturnsOkStatus 测试失败,因为我没有提供任何身份验证和 /accounts 路由映射到的方法被注释预授权注释,但是正在调用该方法并绕过预授权检查。
MyControllerTest.java
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:applicationContext-test.xml" })
public class MyControllerTest {
private MockMvc mockMvc;
@Mock
private MyService myService;
@InjectMocks
private MyController myController;
@Before
public void init() {
MockitoAnnotations.initMocks(this);
mockMvc = MockMvcBuilders.standaloneSetup(myController).build();
}
@Test
public void getAccounts_ReturnsOkStatus() throws Exception {
// Make the GET request, and verify that it returns HttpStatus.OK (200)
mockMvc.perform(MockMvcRequestBuilders.get("/accounts"))
.andExpect(MockMvcResultMatchers.status().isOk());
}
}
applicationContext-test.xml
<sec:global-method-security pre-post-annotations="enabled" />
<bean id="applicationContextProvider" class="com.myapp.springsupport.ApplicationContextProvider" />
<sec:authentication-manager alias="authenticationManager">
<sec:authentication-provider>
<sec:user-service>
<sec:user name="test-superuser" password="test-pwd" authorities="ROLE_SUPERUSER" />
</sec:user-service>
</sec:authentication-provider>
</sec:authentication-manager>
MyController.java
@PreAuthorize("isAuthenticated() and hasRole('ROLE_SUPERUSER')")
@RequestMapping(value = "/accounts", method = RequestMethod.GET)
@ResponseBody
public Collection<Account> getAccounts() {
return new ArrayList<Account>();
}
肯定使用了 applicationContext-test 应用程序上下文,因为使用
手动验证Authentication auth = new UsernamePasswordAuthenticationToken(name, password);
SecurityContextHolder.getContext().setAuthentication(am.authenticate(auth));
仅适用于测试配置中指定的用户凭据(在没有任何其他身份验证提供程序的情况下)。此外,我可以确定预授权被忽略了,因为我已经测试过使用 SEL 调用方法并进行了调试。
我错过了什么?
您必须在构建 mockMvc
时启用 spring 安全性以进行测试。
在Spring4中是这样的:
mockMvc = MockMvcBuilders.webAppContextSetup(context)
.apply(springSecurity())
.build();
在Spring3中是这样的:
@Autowired
private Filter springSecurityFilterChain;
...
mockMvc = MockMvcBuilders.webAppContextSetup(context)
.addFilters(springSecurityFilterChain)
.build();
有关详细信息,请参阅:
- https://spring.io/blog/2014/05/23/preview-spring-security-test-web-security
- How do I unit test spring security @PreAuthorize(hasRole)?
- Spring MVC integration tests with Spring Security
MockMvcBuilders.standaloneSetup
获得手动实例化的 MyController
(没有 Spring,因此没有 AOP)。因此 @PreAuthorize
不会被截取并且跳过安全检查。因此,您可以 @Autowire 您的控制器并将其传递给 MockMvcBuilders.standaloneSetup
以 Mock MyService
使用 @MockBean
以便服务的每个实例都被 Mock 替换。