如何将mockmvc用户关联到安全上下文持有者

问题描述 投票:0回答:1

我无法将我传递到mockmvc get请求的用户关联到安全上下文持有者,因此我无法创建一个模拟原则。

@Import(SecurityConfig::class)
@WebMvcTest(controllers = [MovieController::class])
class MovieControllerTests {
    private lateinit var mockMvc: MockMvc

    @MockBean
    private lateinit var accountRepository: AccountRepository

    @MockBean
    private lateinit var movieRepository: MovieRepository

    @BeforeEach
    fun setup() {
        mockMvc = MockMvcBuilders.standaloneSetup(MovieController(movieRepository, accountRepository))
            .apply<StandaloneMockMvcBuilder?>(SecurityMockMvcConfigurers.springSecurity()).build()
    }
    @Test
    fun `does return movie queue`() {
        mockMvc.get("/movies/fetch-queue") {
            with(user("donna"))
        }.andExpectAll {
            status {
                isOk()
            }
            content {
                contentType(MediaType.APPLICATION_JSON)
            }
        }
        Mockito.verify(accountRepository).findAccountByUsername("donna")
    }
}

我已阅读文档,说我需要手动将 securitycontextpersistencefilter 添加到mockmvc 实例,但我不知道具体该怎么做。该文档还指出,模拟用户只能通过将用户与 HttpServletRequest 关联来起作用。

spring-boot kotlin unit-testing spring-test mockmvc
1个回答
0
投票

在测试中修改SecurityContext的最简单方法是使用@WithMockUser。该注解允许您直接设置用户名、角色和权限。

@Test
@WithMockUser(username = "testuser", roles = {"USER"})
public void testWithMockUser() {
    // Test code here
}

使用@WithSecurityContext创建自定义安全上下文如果您需要更高级的自定义(例如,设置特定的身份验证详细信息),您可以创建自定义注释并实现SecurityContextFactory。

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@WithSecurityContext(factory = CustomSecurityContextFactory.class)
public @interface WithCustomUser {
    String username() default "customuser";
    String[] roles() default {"USER"};
}

实现安全上下文工厂

public class CustomSecurityContextFactory implements WithSecurityContextFactory<WithCustomUser> {

@Override
public SecurityContext createSecurityContext(WithCustomUser customUser) {
    SecurityContext context = SecurityContextHolder.createEmptyContext();

    List<GrantedAuthority> authorities = Arrays.stream(customUser.roles())
        .map(SimpleGrantedAuthority::new)
        .collect(Collectors.toList());

    Authentication auth = new UsernamePasswordAuthenticationToken(
        customUser.username(), "password", authorities);
    context.setAuthentication(auth);

    return context;
}
}

事实上,Spring Security 使用基于 ThreadLocal 的 SecurityContextHolder 在测试和运行时存储和检索 SecurityContext。此方法可确保安全上下文的范围仅限于当前线程。想了解更多详情请看这里

org.springframework.security.test.context.TestSecurityContextHolder

Spring Security 中的这种 ThreadLocal 策略仅限于高级用例,例如将安全上下文传播到子线程或处理反应性和异步场景;但是,InheritableThreadLocal 或自定义策略等替代方案可以解决这些限制。

© www.soinside.com 2019 - 2024. All rights reserved.