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();

有关详细信息,请参阅:

MockMvcBuilders.standaloneSetup 获得手动实例化的 MyController(没有 Spring,因此没有 AOP)。因此 @PreAuthorize 不会被截取并且跳过安全检查。因此,您可以 @Autowire 您的控制器并将其传递给 MockMvcBuilders.standaloneSetup 以 Mock MyService 使用 @MockBean 以便服务的每个实例都被 Mock 替换。