当前位置:  开发笔记 > 编程语言 > 正文

如何对弹簧安全性进行单元测试@PreAuthorize(hasRole)?

如何解决《如何对弹簧安全性进行单元测试@PreAuthorize(hasRole)?》经验,为你挑选了1个好方法。

为了在控制器方法上对PreAuthorize注释的hasRole部分进行单元测试,我需要什么?

我的测试应该成功,因为登录用户只有两个角色中的一个,但它失败并出现以下断言错误:

java.lang.AssertionError:状态

预计:401

实际:200

我在MyController中有以下方法:

@PreAuthorize(value = "hasRole('MY_ROLE') and hasRole('MY_SECOND_ROLE')")
@RequestMapping(value = "/myurl", method = RequestMethod.GET)
public String loadPage(Model model, Authentication authentication, HttpSession session) {
    ...stuff to do...
}

我创建了以下abstract-security-test.xml:




    

    
        
            
                
            
        
    


在我的单元测试中我有这个:

@ContextConfiguration("classpath:/spring/abstract-security-test.xml")
public class MyTest {
    private final MyController myController = new MyController();
    @Autowired
    private AuthenticationManager manager;

    @Test
    public void testValidUserWithInvalidRoleFails() throws Exception {
        MockMvc mockMvc = standaloneSetup(myController).setViewResolvers(viewResolver()).build();

        Authentication auth = login("missingsecondrole", "user");

        mockMvc.perform(get("/myurl")
            .session(session)
            .flashAttr(MODEL_ATTRIBUTE_NAME, new ModelMap())
            .principal(auth)).andExpect(status().isUnauthorized());
    }

    protected Authentication login(String name, String password) {
        Authentication auth = new UsernamePasswordAuthenticationToken(name, password);
        SecurityContextHolder.getContext().setAuthentication(manager.authenticate(auth));
        return auth;
    }

    private ViewResolver viewResolver() {
        InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
        viewResolver.setPrefix("WEB-INF/views");
        viewResolver.setSuffix(".jsp");
        return viewResolver;
    }
}

Rob Winch.. 27

UPDATE

Spring Security 4为与MockMvc集成提供全面支持.例如:

import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.*;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
@WebAppConfiguration
public class SecurityMockMvcTests {

    @Autowired
    private WebApplicationContext context;

    private MockMvc mvc;

    @Before
    public void setup() {
        mvc = MockMvcBuilders
                .webAppContextSetup(context)
                .apply(springSecurity())
                .build();
    }

    @Test
    public void withUserRequestPostProcessor() {
        mvc
            .perform(get("/admin").with(user("admin").roles("USER","ADMIN")))
            ...
    }

    @WithMockUser(roles="ADMIN")
    @Test
    public void withMockUser() {
        mvc
            .perform(get("/admin"))
            ...
    }

 ...

问题

问题是设置SecurityContextHolder在此实例中不起作用.原因是SecurityContextPersistenceFilter将使用SecurityContextRepository尝试从HttpServletRequest中找出SecurityContext(默认情况下它使用HttpSession).找到(或找不到)的SecurityContext将覆盖您在SecurityContextHolder上设置的SecurityContext.

解决方案

要确保对请求进行身份验证,您需要使用您正在利用的SecurityContextRepository关联SecurityContext.默认值为HttpSessionSecurityContextRepository.允许您模拟用户登录的示例方法如下:

private SecurityContextRepository repository = 
      new HttpSessionSecurityContextRepository();

private void login(SecurityContext securityContext, HttpServletRequest request) {
    HttpServletResponse response = new MockHttpServletResponse();

    HttpRequestResponseHolder requestResponseHolder = 
          new HttpRequestResponseHolder(request, response);
    repository.loadContext(requestResponseHolder);

    request = requestResponseHolder.getRequest();
    response = requestResponseHolder.getResponse();

    repository.saveContext(securityContext, request, response);
}

由于您可能不知道如何访问MockMvc中的HttpServletRequest,因此如何使用它的细节可能仍然有点模糊,但请继续阅读,因为有更好的解决方案.

让它更容易

如果您想更轻松地与MockMvc进行此安全相关的交互,可以参考gs-spring-security-3.2示例应用程序.在项目中,您将找到一些用于处理Spring Security和MockMvc的实用程序,称为SecurityRequestPostProcessors.要使用它们,您可以将之前提到的类复制到项目中.使用此实用程序将允许您编写类似这样的内容:

RequestBuilder request = get("/110")
    .with(user(rob).roles("USER"));

mvc
    .perform(request)
    .andExpect(status().isUnAuthorized());

注意:无需在请求上设置主体,因为只要用户通过身份验证,Spring Security就会为您建立Principal.

您可以在SecurityTests中找到其他示例.该项目还将协助MockMvc和Spring Security之间的其他集成(即在执行POST时使用CSRF令牌设置请求).

默认不包括在内?

您可能会问为什么默认情况下不包含此内容.答案是我们根本没有时间进行3.2时间表.示例中的所有代码都可以正常工作,但我们对命名约定以及它如何集成以释放它没有足够的信心.您可以跟踪计划推出Spring Security 4.0.0.M1的SEC-2015.

更新

您的MockMvc实例还需要包含springSecurityFilterChain.为此,您可以使用以下内容:

@Autowired
private Filter springSecurityFilterChain;

@Test
public void testValidUserWithInvalidRoleFails() throws Exception {
    MockMvc mockMvc = standaloneSetup(myController)
        .addFilters(springSecurityFilterChain)
        .setViewResolvers(viewResolver())
        .build();
    ...

为了@Autowired工作,您需要确保包含使您的springSecurityFilterChain成为的安全配置@ContextConfiguration.对于当前的设置,这意味着"classpath:/spring/abstract-security-test.xml"应该包含您的安全配置部分(以及所有依赖bean).或者,也可以包括在第二个文件(S)@ContextConfiguration中有您的安全配置(和所有相关的bean)的一部分.



1> Rob Winch..:
UPDATE

Spring Security 4为与MockMvc集成提供全面支持.例如:

import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.*;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
@WebAppConfiguration
public class SecurityMockMvcTests {

    @Autowired
    private WebApplicationContext context;

    private MockMvc mvc;

    @Before
    public void setup() {
        mvc = MockMvcBuilders
                .webAppContextSetup(context)
                .apply(springSecurity())
                .build();
    }

    @Test
    public void withUserRequestPostProcessor() {
        mvc
            .perform(get("/admin").with(user("admin").roles("USER","ADMIN")))
            ...
    }

    @WithMockUser(roles="ADMIN")
    @Test
    public void withMockUser() {
        mvc
            .perform(get("/admin"))
            ...
    }

 ...

问题

问题是设置SecurityContextHolder在此实例中不起作用.原因是SecurityContextPersistenceFilter将使用SecurityContextRepository尝试从HttpServletRequest中找出SecurityContext(默认情况下它使用HttpSession).找到(或找不到)的SecurityContext将覆盖您在SecurityContextHolder上设置的SecurityContext.

解决方案

要确保对请求进行身份验证,您需要使用您正在利用的SecurityContextRepository关联SecurityContext.默认值为HttpSessionSecurityContextRepository.允许您模拟用户登录的示例方法如下:

private SecurityContextRepository repository = 
      new HttpSessionSecurityContextRepository();

private void login(SecurityContext securityContext, HttpServletRequest request) {
    HttpServletResponse response = new MockHttpServletResponse();

    HttpRequestResponseHolder requestResponseHolder = 
          new HttpRequestResponseHolder(request, response);
    repository.loadContext(requestResponseHolder);

    request = requestResponseHolder.getRequest();
    response = requestResponseHolder.getResponse();

    repository.saveContext(securityContext, request, response);
}

由于您可能不知道如何访问MockMvc中的HttpServletRequest,因此如何使用它的细节可能仍然有点模糊,但请继续阅读,因为有更好的解决方案.

让它更容易

如果您想更轻松地与MockMvc进行此安全相关的交互,可以参考gs-spring-security-3.2示例应用程序.在项目中,您将找到一些用于处理Spring Security和MockMvc的实用程序,称为SecurityRequestPostProcessors.要使用它们,您可以将之前提到的类复制到项目中.使用此实用程序将允许您编写类似这样的内容:

RequestBuilder request = get("/110")
    .with(user(rob).roles("USER"));

mvc
    .perform(request)
    .andExpect(status().isUnAuthorized());

注意:无需在请求上设置主体,因为只要用户通过身份验证,Spring Security就会为您建立Principal.

您可以在SecurityTests中找到其他示例.该项目还将协助MockMvc和Spring Security之间的其他集成(即在执行POST时使用CSRF令牌设置请求).

默认不包括在内?

您可能会问为什么默认情况下不包含此内容.答案是我们根本没有时间进行3.2时间表.示例中的所有代码都可以正常工作,但我们对命名约定以及它如何集成以释放它没有足够的信心.您可以跟踪计划推出Spring Security 4.0.0.M1的SEC-2015.

更新

您的MockMvc实例还需要包含springSecurityFilterChain.为此,您可以使用以下内容:

@Autowired
private Filter springSecurityFilterChain;

@Test
public void testValidUserWithInvalidRoleFails() throws Exception {
    MockMvc mockMvc = standaloneSetup(myController)
        .addFilters(springSecurityFilterChain)
        .setViewResolvers(viewResolver())
        .build();
    ...

为了@Autowired工作,您需要确保包含使您的springSecurityFilterChain成为的安全配置@ContextConfiguration.对于当前的设置,这意味着"classpath:/spring/abstract-security-test.xml"应该包含您的安全配置部分(以及所有依赖bean).或者,也可以包括在第二个文件(S)@ContextConfiguration中有您的安全配置(和所有相关的bean)的一部分.

推荐阅读
Gbom2402851125
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有