Spring Boot:使用 Junit 模拟具有安全上下文的用户身份验证
Posted
技术标签:
【中文标题】Spring Boot:使用 Junit 模拟具有安全上下文的用户身份验证【英文标题】:Spring Boot : Mock user authentication with security context using Junit 【发布时间】:2020-10-10 05:01:03 【问题描述】:我想为使用 JWT 令牌验证的 Rest API 创建 JUnit 5 测试:
@Test
public void resetTokenTest_OK() throws Exception
when(userService.findByResetPasswordToken(anyString())).thenReturn(trueOptional);
mockMvc.perform(post("/users/reset_token")
.contentType(MediaType.APPLICATION_JSON)
.content(ResetPasswordTokenDTO))
.andExpect(status().isOk())
.andExpect(jsonPath("$.resetPasswordToken").value(randomString_254))
.andExpect(jsonPath("$.login").value("login"))
.andExpect(jsonPath("$.status").value("200"))
.andExpect(jsonPath("$.error").value(""))
.andExpect(jsonPath("$.errorDescription").value(""));
GitHub
我使用此有效负载发出 Mockup 请求:
"resetPasswordToken": "eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0.VFb0qJ1LRg_4ujbZoRMXnVkUgiuKq5KxWqNdbKq_G9Vvz-S1zZa9LPxtHWKa64zDl2ofkT8F6jBt_K4riU-fPg",
"login": "login",
"status": "1",
"error": "",
"errorDescription": ""
我得到了这个line的NPE:
Caused by: java.lang.NullPointerException
at org.engine.utils.GenericUtils.getLoggedInUser(GenericUtils.java:15)
at org.engine.rest.UsersController.lambda$resetToken$0(UsersController.java:159)
at java.base/java.util.Optional.map(Optional.java:258)
at org.engine.rest.UsersController.resetToken(UsersController.java:153)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:564)
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:190)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:138)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:105)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:879)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:793)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1040)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:943)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)
完整的Log:
我得到了这段代码的 NPE:
return (Users) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
GitHub
我如何模拟来自SecurityContextHolder.getContext().getAuthentication().getPrincipal()
的结果以免获得 NPE?
【问题讨论】:
不要嘲笑它;请改用 Spring Security 测试支持(例如,@WithUser
)。
我需要把它放在哪里?
试着把它放在失败的测试之上。
【参考方案1】:
有多种方法可以使用@WithMockUser
、@WithAnonymousUser
、@WithUserDetails
、@WithSecurityContext
来模拟安全性。您可以将这些注释与@Test
方法一起使用
@WithMockUser
SecurityContext
中填充的身份验证是 UsernamePasswordAuthenticationToken
类型
Authentication 的主体是 Spring Security 的 User 对象
用户将拥有 "user" 的用户名,密码 "password",以及名为 "ROLE_USER" 的单个 GrantedAuthority用过。
您可以自定义用户名、角色、值等参数
@WithAnonymousUser
使用@WithAnonymousUser
允许以匿名用户身份运行。当您希望以特定用户运行大部分测试,但又想以匿名用户身份运行一些测试时,这尤其方便。例如,以下将使用 @WithMockUser
和 以匿名用户身份匿名运行 withMockUser1 和 withMockUser2。
@WithUserDetails
虽然@WithMockUser
是一种非常方便的入门方式,但它可能并非在所有情况下都有效。例如,应用程序通常期望Authentication
主体属于特定类型。这样做是为了让应用程序可以将主体引用为自定义类型并减少对 Spring Security 的耦合。
自定义主体通常由自定义UserDetailsService
返回,该自定义主体返回实现UserDetails
和自定义类型的对象。对于这种情况,使用自定义 UserDetailsService 创建测试用户很有用。这正是@WithUserDetails
所做的。
@WithSecurityContext
我们可以创建自己的注解,使用@WithSecurityContext 来创建我们想要的任何SecurityContext。例如,我们可以创建一个名为@WithMockCustomUser 的注解为@WithSecurityContext(factory = WithMockCustomUserSecurityContextFactory.class)
【讨论】:
以上是关于Spring Boot:使用 Junit 模拟具有安全上下文的用户身份验证的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 JUnit 在 Spring Boot 中的公共方法中模拟私有方法
在 JUnit 5 测试中模拟 Spring Boot 2 应用程序的自动装配依赖项
@MockBean 不适用于带有 JUnit 5 和 Spring Boot 2 的 @WebMvcTest?
使用 JUnit 5 的 spring-boot-starter-test