在Spring Security Test中模拟自定义用户

时间:2017-09-11 19:28:23

标签: spring-mvc spring-security junit4

我们正在使用Spring 4.3.9.RELEASE和Spring Security 4.2.3.RELEASE,所以这些是我们见过的一些最新版本。我们有一个RESTful(spring-mvc)后端,我们使用Spring Web Security进行基于角色的API访问。

我们有一个如下控制器:

@RequestMapping(value = "/create", method = RequestMethod.POST, produces = "application/json", headers = "content-type=application/json")
public @ResponseBody MyObjectEntity createMyObject(@RequestBody MyObjectEntity myObj) throws MyObjectException
{
    UserDetails userDetails = (UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
    CustomUser user = null;
    if (userDetails instanceof CustomUser)
    {
        user = ((CustomUser) userDetails);
    }
    String email = user.getEmail();
    MyObjectEntity myObj = MyObjectService.createMyObject(myObj, email);
    if (SecurityContextHolder.getContext().getAuthentication() != null)
    {
        SecurityContextHolder.getContext().setAuthentication(null);
    }
    return myObj;
}

我们知道用户已使用用户名和密码从网站登录。我们知道UI有一个令牌,它们在标题中传递它。我们的安全性使用SiteMinder示例,这意味着我们有一个UserDetailsS​​ervice转发给第三方,传递令牌,我们现在拥有用户名,密码和用户拥有的角色。这通常运作良好。 我们确实创建了一个CustomUserDetailsS​​ervice,如下所示:

public class CustomUserDetailsService implements UserDetailsService
{
     @Override
     public UserDetails loadUserByUsername(String accessToken) throws 
       UsernameNotFoundException, 
       PreAuthenticatedCredentialsNotFoundException
       {
            // goto to third-party service to verify token
            // get the Custom User and the user roles
            // also get some extra data, so a custom user
       }
}

因此,一旦我们建立了令牌有效,并且我们从该第三方获得了额外的用户信息,并且我们拥有该API授权的有效角色......那么我们就可以执行控制器本身。我们看到这个代码是传统的,可以让现有用户脱离Spring Security Context。

 UserDetails userDetails = (UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
    CustomUser user = null;
    if (userDetails instanceof CustomUser)
    {
        user = ((CustomUser) userDetails);
    }

实际上,根据我们已经阅读过的内容,当您拥有自定义用户和CustomUserDetails时,这是实现此目的的方法。使用此代码,我们希望获得此用户的电子邮件。当我们使用Advanced REST Client实际测试API时,这一切都有效。我们的QA必须针对网站进行身份验证,并且他们将令牌传递回UI,他们获得这些访问令牌,并将它们放在高级REST客户端(或邮递员)的标题中,这一切都有效。

我们甚至有代码在API结束时使安全上下文无效。

if (SecurityContextHolder.getContext().getAuthentication() != null)
{
     SecurityContextHolder.getContext().setAuthentication(null);
}

反对,真正的API,真正的进步,这很有效。 现在,在测试时,一些测试对我们的安全控制器起作用,而另一些则不然。所以,这里我们有一个控制器来测试:

@RequestMapping(value = "/{productId}", method = RequestMethod.GET, headers = "Accept=application/json")
public @ResponseBody ProductEntity getProductById(@PathVariable("productId") long productId)
{
    logger.debug("ProductController: getProductById: productId=" + productId);
    CustomUser user = authenticate();
    ProductEntity productEntity = service.getById(productId);
    logger.debug("ProductController: getProductById: productEntity=" + productEntity);
    invalidateUser();
    return productEntity;
}

这是测试:

@Test
public void testMockGetProductByProductId() throws Exception
{
    MockHttpServletRequestBuilder requestBuilder = MockMvcRequestBuilders.get(BASE_URL + "/1").with(user("testuser").roles("REGULAR_USER"));
    this.mockMvc.perform(requestBuilder).andDo(print()).andExpect(status().isOk());
}

这是有效的,因为即使我们到达控制器,我们也不需要CustomerUser设置,因此它可以工作。如果角色是正确的角色(" REGULAR_USER"),那么它可以正常工作,如果角色不正确,我们会收到403错误。

但是如果你看看我首先在顶部发布的控制器,我们需要设置CustomUser,如果没有设置,那么当我们尝试收到该电子邮件时,我们就会失败。因此,我们一直在寻找在身份验证中设置模拟用户的多种方法,因此当我们访问Controller时,我们可以在安全上下文中获取该CustomUser。

我之前实际上已经这样做了,但那时我们使用的是标准的spring安全用户,而不是自定义用户。

我们绝对可以在安全上下文中建立一个CustomUser,但是当它到达控制器时,这个代码就会运行....

// THIS WORKS
UserDetails userDetails = (UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
    CustomUser user = null;

    // This IF fails because;
    // userDetails is of instance User (Spring Security User)
    // and not CustomUser.
    if (userDetails instanceof CustomUser)
    {
        user = ((CustomUser) userDetails);
    }

让我添加我们的CustomUser代码:

public class CustomUser implements UserDetails
{

private static final long serialVersionUID = -6650061185298405641L;
private String userName;
private ArrayList<GrantedAuthority> authorities;

private String firstName;
private String middleName;
private String lastName;
private String email;
private String phone;
private String externalUserId;

  // getters/setters
  // toString

}

我希望我在这里提供足够的信息,有人可以回答我的问题。我花了一两天时间在互联网上寻找能够回答这个问题的人无济于事。从Spring 3和更早的Spring Security 3.x开始,一些答案有点老了。如果需要更多信息,请告诉我。谢谢!

我想知道......如果我需要一个包含UserDetails的CustomUserDetails?

再次感谢!

1 个答案:

答案 0 :(得分:5)

这可能比你想象的容易得多。

CustomUser userDetails = new CustomUser();
/* TODO: set username, authorities etc */
MockHttpServletRequestBuilder requestBuilder = MockMvcRequestBuilders.get(BASE_URL + "/1").with(user(userDetails));

只要您的CustomUser实施UserDetails界面,就可以使用此功能。