小编典典

使用Spring Security进行单元测试

spring

我公司一直在评估Spring MVC,以确定我们是否应在下一个项目中使用它。到目前为止,我喜欢我所看到的内容,现在,我正在查看Spring Security模块,以确定是否可以/应该使用它。

我们的安全要求非常基本。用户只需要能够提供用户名和密码即可访问网站的某些部分(例如获取有关其帐户的信息);并且网站上的页面很少(常见问题解答,支持等),应该为匿名用户提供访问权限。

在我创建的原型中,我已经在Session中为经过身份验证的用户存储了一个“ LoginCredentials”对象(其中仅包含用户名和密码)。例如,某些控制器检查该对象是否在会话中,以获取对登录用户名的引用。我正在寻找用Spring Security替代这种自家的逻辑,这将具有消除“我们如何跟踪登录用户的方式”的好处。和“我们如何验证用户身份?” 从我的控制器/业务代码。

看来Spring Security提供了(每线程)“上下文”对象,以便能够从你应用程序中的任何位置访问用户名/主要信息…

Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();

…在某种程度上,这似乎是非Spring的,就像这个对象是一个(全局)单例。

我的问题是这样的:如果这是在Spring Security中访问有关已认证用户的信息的标准方法,那么将Authentication对象注入SecurityContext的公认方法是什么,以便当单元测试需要一个认证用户?

我是否需要在每个测试用例的初始化方法中进行连接?

protected void setUp() throws Exception {
    ...
    SecurityContextHolder.getContext().setAuthentication(
        new UsernamePasswordAuthenticationToken(testUser.getLogin(), testUser.getPassword()));
    ...
}

这似乎过于冗长。有没有更简单的方法?

SecurityContextHolder对象本身看起来非常不像Spring …


阅读 1453

收藏
2020-04-13

共2个答案

小编典典

问题在于,Spring Security不会使Authentication对象作为容器中的bean可用,因此无法轻松地将其注入或自动接线。

在开始使用Spring Security之前,我们将在容器中创建一个会话范围的bean来存储Principal,将其注入到“ AuthenticationService”(单身)中,然后将该bean注入到其他需要了解当前Principal的服务中。

如果要实现自己的身份验证服务,则基本上可以执行以下操作:创建一个具有“ principal”属性的会话范围的Bean,将其注入到身份验证服务中,让auth服务在成功auth上设置该属性,然后根据需要使auth服务可用于其他bean。

对于使用SecurityContextHolder,我不会感到很糟糕。虽然。我知道这是静态的/ Singleton,Spring不鼓励使用此类东西,但它们的实现会根据环境适当地进行行为:会话范围在Servlet容器中,线程范围在JUnit测试中,等等。真正的限制因素Singleton的含义是它提供对不同环境不灵活的实现。

2020-04-13
小编典典

只需按照通常的方式进行操作,然后SecurityContextHolder.setContext()在测试类中使用即可将其插入,例如:

控制器:

Authentication a = SecurityContextHolder.getContext().getAuthentication();

测试:

Authentication authentication = Mockito.mock(Authentication.class);
// Mockito.whens() for your authorization object
SecurityContext securityContext = Mockito.mock(SecurityContext.class);
Mockito.when(securityContext.getAuthentication()).thenReturn(authentication);
SecurityContextHolder.setContext(securityContext);
2020-04-13