第八章 Spring Security 的测试支持
本章节只介绍 Spring Security 提供的测试支持,包含哪些关键的注解、如何设置Security测试环境、以及注解的作用。
在下一章节会介绍如何通过重写 @WithMockUser 来使用自定义的 UserDetails 对象。
官网上的是 Spring Security 与原始的 Spring Test 环境的集成,这里就不做赘述了,我们使用的是 SpringBoot Test 环境
一、设置测试环境
需要先引入 spring-security-test 依赖,这样就可以直接在 @SpringBootTest 中集成 Security 的测试环境,就是这么简单.
@RunWith(SpringRunner.class)
@SpringBootTest
public class DemoTest {
// .. 一些 JUnit 单元测试方法
}
二、常用注解
单元测试要尽量做到“单元”化,只测试一个具体的功能,而不用将MVC、DAO各层都测试一遍。
因此这些注解可以帮助我们建立一个模拟的测试环境,使我们的测试代码只关注业务是否正常执行,代码是否正确。而不用考虑用户是否登陆,是否需要重新授权等问题。
1. @WithMockUser
在方法上使用一个模拟的用户,而不用真的去注册并登陆。
该注解会 Mock 一个用户名为‘user’,密码为 ’password‘,角色为 ’ROLE_USER’ 的Authentication,并将其注入到 SecurityContext 中。
@Test
@WithMockUser
public void getMessageByUser() {
// 查询SecurityContext 上下文环境,可以取到 Mock 的模拟用户数据
String username = ((UserDetails)SecurityContextHolder.getSecurityContext().getAuthentication().getPrincipal()).getUsername();
// ......
}
思考:如果我们使用的是自定义的 CustomUserDetails 呢?比如扩展了 UserDetails 的属性,增加了 level 等级属性,那么将如何Mock并从上下文中获取呢? –这将会在下一节“自定义注解”中说明
2. @WithAnonymousUser
当需要用户登录,但是不需要用户的信息时,可以考虑以匿名用户的身份运行一些测试。这样会更加方便。
3. @WithUserDetails
使用自定义的 UserDetailsService 来创建身份验证的主题,但是需要用户存在。在正常业务中,可能还涉及到数据库查询,如果包含数据库查询,还需要与业务逻辑的数据库隔离等等问题。
自定义查找的用户名,以及自定义用来查找的 UserDetailsService。
@WithUserDetails(value = "customUsername", userDetailsServiceBeanName = "myUserDetailsService")
三、完整的测试案例
UserService 用户业务服务,提供一个创建用户的功能,并且在保存之前,记录当前创建人。
// 用户业务服务
@Service
public class UserService {
// 创建用户,并且指定创建人与创建时间
public User createUser(User user){
// 从当前环境中取出当前主体的用户名
String username = ((UserDetails)SecurityContextHolder.getSecurityContext().getAuthentication().getPrincipal()).getUsername();
// 保存新建用户的创建者、创建时间
user.setCreateUser(username);
user.setCreateDate(new Date());
return save(user);
}
}
测试用户在创建时,是否设置了创建人(即@WithMockUser 是否把用户注入了SecurityContext中)。
@Runwith(SpringRunner.class)
@SpringBootTest
public class UserServiceTest {
@Autowired
UserService userService;
@Test
@WithMockUser(username = "admin", roles = {"ADMIN","USER"})
public void test(){
User user = userService.create(new User("张三"));
Assertions.assertThat(user.getCreateUser()).isEqualsTo("admin");
}
}