【问题标题】:PreAuthorize annotations on Spring controllers are ignored when testing测试时忽略 Spring 控制器上的 PreAuthorize 注释
【发布时间】:2017-06-24 13:23:57
【问题描述】:

我正在使用 JUnit、Mockito 和 spring-test 测试我的 Spring MVC 控制器。不幸的是,测试忽略了我的控制器方法上的 @PreAuthorize 注释,我无法解决这个问题。下面是关键代码 sn-ps,尽管我已经删除了用于模拟来自 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 进行了测试以调用方法并进行了调试。

我错过了什么?

【问题讨论】:

    标签: java spring spring-security mockito junit4


    【解决方案1】:

    在构建 mockMvc 时,您必须启用 Spring Security 进行测试。

    在 Spring 4 中是这样的:

    mockMvc = MockMvcBuilders.webAppContextSetup(context)
                             .apply(springSecurity())
                             .build();
    

    在 Spring 3 中是这样的:

    @Autowired
    private Filter springSecurityFilterChain;
    ...
    mockMvc = MockMvcBuilders.webAppContextSetup(context)
                             .addFilters(springSecurityFilterChain)
                             .build();
    

    更多详情见:

    【讨论】:

    • 谢谢。我现在遇到的主要问题是使用“webAppContextSetup”而不是“standaloneSetup”需要我使用组件扫描将我的控制器包含在 applicationContext 中。然而,这样做需要我还包括所有自动装配到我的控制器中的类,即使它们都在测试中被模拟并且实现本身没有被使用。我的设置显然有问题,但我不知道是什么问题。
    • 看看docs.spring.io/spring-security/site/docs/current/reference/html/…。它解释了如何仍然可以使用standaloneSetup。使用 webAppContextSetup 时,请查看 @MockBean (docs.spring.io/spring-boot/docs/current/api/org/springframework/…),它可用于模拟控制器中的自动装配 bean。
    • @RenéScheibe 服务层上的注解怎么样?
    【解决方案2】:

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

    【讨论】:

      猜你喜欢
      • 2022-10-20
      • 2011-07-21
      • 2013-03-10
      • 1970-01-01
      • 2015-04-30
      • 1970-01-01
      • 1970-01-01
      • 2015-08-12
      • 2021-11-11
      相关资源
      最近更新 更多