使用 keycloak 进行 Spring Boot 测试

2024-03-19

我正在尝试运行简单的单元测试,Keycloak 以正确的方式配置(我测试了它,我的 mvc 应用程序正在连接并且用户已通过身份验证_但现在我尝试测试我的控制器,即使 我使用 spring slices keycloak 适配器被调用并给我错误。适配器配置主要来自keycloak文档

@WebMvcTest(UserController.class)
class UserControllerTest {



    @MockBean
    UserService userService;

    @Autowired
    MockMvc mockMvc;

    @BeforeEach
    void setUp() {
    }

    @AfterEach
    void tearDown() {
        reset(userService);
    }

    @Test
    void logout() throws Exception {
        mockMvc.perform(get("/logout"))
                .andExpect(status().isOk());
    }
}

但当我尝试运行它时出现错误,堆栈跟踪:

java.lang.NullPointerException
    at org.keycloak.adapters.KeycloakDeploymentBuilder.internalBuild(KeycloakDeploymentBuilder.java:57)
    at org.keycloak.adapters.KeycloakDeploymentBuilder.build(KeycloakDeploymentBuilder.java:205)
    at org.keycloak.adapters.springboot.KeycloakSpringBootConfigResolver.resolve(KeycloakSpringBootConfigResolver.java:37)
    at org.keycloak.adapters.springsecurity.config.KeycloakSpringConfigResolverWrapper.resolve(KeycloakSpringConfigResolverWrapper.java:40)
    at org.keycloak.adapters.AdapterDeploymentContext.resolveDeployment(AdapterDeploymentContext.java:89)
    at org.keycloak.adapters.springsecurity.filter.KeycloakPreAuthActionsFilter.doFilter(KeycloakPreAuthActionsFilter.java:81)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
    at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:74)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:118)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
    at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:105)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
    at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:56)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:118)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
    at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:215)
    at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:178)
    at org.springframework.mock.web.MockFilterChain.doFilter(MockFilterChain.java:133)
    at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:118)
    at org.springframework.mock.web.MockFilterChain.doFilter(MockFilterChain.java:133)
    at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:92)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:118)
    at org.springframework.mock.web.MockFilterChain.doFilter(MockFilterChain.java:133)
    at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:93)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:118)
    at org.springframework.mock.web.MockFilterChain.doFilter(MockFilterChain.java:133)
    at org.springframework.test.web.servlet.MockMvc.perform(MockMvc.java:182)
    at org.czekalski.userkeycloak.controller.UserControllerTest.logout(UserControllerTest.java:50)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:532)
    at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:115)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$6(TestMethodTestDescriptor.java:171)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:72)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:167)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:114)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:59)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$4(NodeTestTask.java:108)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:72)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:98)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:74)
    at java.util.ArrayList.forEach(ArrayList.java:1257)
    at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$4(NodeTestTask.java:112)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:72)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:98)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:74)
    at java.util.ArrayList.forEach(ArrayList.java:1257)
    at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$4(NodeTestTask.java:112)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:72)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:98)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:74)
    at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:32)
    at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
    at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:51)
    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:220)
    at org.junit.platform.launcher.core.DefaultLauncher.lambda$execute$6(DefaultLauncher.java:188)
    at org.junit.platform.launcher.core.DefaultLauncher.withInterceptedStreams(DefaultLauncher.java:202)
    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:181)
    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:128)
    at com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:74)
    at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)

添加到测试@TestPropertySource("classpath:secTest.properties")secTest.properties 里面的位置keycloak.enabled = false没有帮助

测试中的代码:

@Controller
public class UserController {

    private final UserService userService;

    public UserController( UserService userService) {

        this.userService = userService;
    }



    @GetMapping("/index")
    public String logout(){
        return "users/logout";
    }


    @GetMapping("/logged")
    public String loggedIn(Model model){
        model.addAttribute("token",   userService.getloggedInUser());
        return "users/logged";
    }

我刚刚写了一组库简化安全 Spring 应用程序的单元测试 https://github.com/ch4mpy/spring-addons.

它包括一个@WithMockKeycloackAuth注释,以及Keycloak专用MockMvc请求后处理器和WebTestClient配置器/修改器

使用示例:

@RunWith(SpringRunner.class)
@WebMvcTest(GreetingController.class)
@ContextConfiguration(classes = GreetingApp.class)
@ComponentScan(basePackageClasses = { KeycloakSecurityComponents.class, KeycloakSpringBootConfigResolver.class })
public class GreetingControllerTests extends ServletUnitTestingSupport {
    @MockBean
    MessageService messageService;

    @Test
    @WithMockKeycloackAuth
    public void whenUserIsNotGrantedWithAuthorizedPersonelThenSecretRouteIsNotAccessible() throws Exception {
        mockMvc().get("/secured-route").andExpect(status().isForbidden());
    }

    @Test
    @WithMockKeycloackAuth("AUTHORIZED_PERSONNEL")
    public void whenUserIsGrantedWithAuthorizedPersonelThenSecretRouteIsAccessible() throws Exception {
        mockMvc().get("/secured-route").andExpect(content().string(is("secret route")));
    }

    @Test
    @WithMockKeycloakAuth(
            authorities = { "USER", "AUTHORIZED_PERSONNEL" },
            id = @IdTokenClaims(sub = "42"),
            oidc = @OidcStandardClaims(
                    email = "[email protected] /cdn-cgi/l/email-protection",
                    emailVerified = true,
                    nickName = "Tonton-Pirate",
                    preferredUsername = "ch4mpy"),
            privateClaims = @ClaimSet(stringClaims = @StringClaim(name = "foo", value = "bar")))
    public void whenAuthenticatedWithKeycloakAuthenticationTokenThenCanGreet() throws Exception {
        mockMvc().get("/greet")
                .andExpect(status().isOk())
                .andExpect(content().string(startsWith("Hello ch4mpy! You are granted with ")))
                .andExpect(content().string(containsString("AUTHORIZED_PERSONNEL")))
                .andExpect(content().string(containsString("USER")));

取决于我建议您希望获得多少工具spring-security-oauth2-test-addons or spring-security-oauth2-test-webmvc-addons来自 Maven 中心:

<dependency>
  <groupId>com.c4-soft.springaddons</groupId>
  <artifactId>spring-security-oauth2-test-addons</artifactId>
  <version>2.3.4</version>
  <scope>test</scope>
</dependency>
<dependency>
  <groupId>com.c4-soft.springaddons</groupId>
  <artifactId>spring-security-oauth2-test-webmvc-addons</artifactId>
  <version>2.3.4</version>
  <scope>test</scope>
</dependency>

如果您只感兴趣的话,第一个就足够了@WithMockKeycloakAuth注解。其次添加了 Fluent API(MockMvc 请求后处理器)和其他内容,例如 MockMvc 包装器,其中包含内容类型和接受标头的默认值

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用 keycloak 进行 Spring Boot 测试 的相关文章

随机推荐

  • 将文件扩展名与程序关联

    我知道怎么做 而且我去过http www codeproject com KB vb VBFileAssociation aspx http www codeproject com KB vb VBFileAssociation aspx前
  • 在python中将pdf转换为docx格式

    请问如何将pdf转换为docx 我尝试使用 pdfminer 转换为 html 来提取文本 但看起来仍然不够好 pdf2docx 安装pdf2docx包点击here https github com dothinking pdf2docx
  • Dialogflow动态实体问题

    我正在尝试使用 Dialogflow api ai 创建一个 Actions on Google 应用程序 我需要获取火车状态 所以我的实体将是火车号码或火车名称 火车的名字有很多 所以我无法手动添加实体值 如何将实体值动态添加到控制台 您
  • Spring WebFlux Webclient 作为 Mono 接收应用程序/八位字节流文件

    我正在 Kotlin 中制作一个小型 Spring WebFlux 应用程序的原型 该应用程序需要从远程 REST 端点获取 tar 存档并将其存储在本地磁盘上 听起来很简单 我首先创建了一个集成测试 该测试启动 Spring 服务器和另一
  • NHibernate Definitive Cascade 应用指南

    是否有任何互联网资源对 NHibernate 的所有级联设置提供明确的指南 其中包括类结构 HBM 以及每个级联设置的操作对与 NH 的所有关系的影响的示例 此外 如果有以最正确的方式完成常见关联的示例 例如设置一个状态表 您将永远不会最终
  • 沸腾的 MSYS shell - 它可以更换吗?

    我需要一个适用于 MSYS 的可用外壳 这是我目前的困境 默认的 rxvt exe 有一个滚动条以及复制和粘贴功能 但不会将控制字符或箭头键发送到 shell 中正在运行的程序 如解释器 调试器 当使用 Haskell 解释器 ghci 时
  • ASP.NET MVC - ID 字段的自定义模型绑定器

    我有以下实体 public class Category public virtual int CategoryID get set Required ErrorMessage Section is required public virt
  • GitoLite 和 Git 可以使用 LDAP 而不是 SSH 密钥吗

    我的公司正在考虑使用 git 和 gitolite 但不想使用 SSH 密钥 而是像使用 LDAP 一样工作 这可以做到吗 我从来没有见过这样做 我在每个地方都有这个设置 并且总是使用 SSH 密钥 是的 您可以毫无问题地做到这一点 你只需
  • Selenium:指向默认 Chrome 会话

    虽然我意识到这不是 好的 做法 我有一个用例 我需要将 Selenium 驱动程序指向 连接 到我的默认 Chrome 会话 配置文件 我的默认配置文件在这里 Library Caches Google Chrome Default 这是我
  • 在 Swift 中从字符串创建 ZIP 文件

    let data InPractiseThisWillBeAReheallyLongString createDir let docsDir FileManager default urls for documentDirectory in
  • 为什么连接到indexedDB时,有时会在onupgradeneeded之前调用onsuccess?

    我在使用 IndexedDB 时遇到问题 在 Firefox 18 上 当我创建新数据库时 onsuccess方法同时被调用有onupgradeneeded 在 Chrome 24 上 这是我想要的行为 onsuccess方法仅在之后调用o
  • android.os.Looper 中的方法 myLooper 未使用协程进行模拟

    我想在 JUnit 中对协程进行一些测试 但遇到了一些问题 代码很简单 Test fun coroutineTest runBlocking Unconfined doesnt work too runBlocking delay 1000
  • 在 Tapestry 5.3 中链接多个选择组件(Ajax 更新)

    我正在使用 Tapestry 5 3 7 并且我想使用 Ajax 链接选择表单元素 如果我在选择元素中选择一个选项 则另一个选择会根据您的第一个选择出现选择另一个元素 我尝试了挂毯文档中的示例 并针对我的项目进行了调整 尽管我的自定义代码非
  • 单元测试错误“未将对象引用设置为对象的实例。”

    在我的控制器中 我想测试控制器是否正在调用存储库方法 这是方法控制器 HttpGet public ActionResult GetModulePropertyName string moduleTypeValue var temp mod
  • 如何根据日历将 JSON 数据分组到杂草中?

    我正在做一些 d3JS 编程来生成带有 JSON 数据的图形 test date 1 5 2014 allocated 14 14 unallocated 7 14 date 1 6 2014 allocated 10 38 unalloc
  • 使用 Cordova 3.7 从 iOS 8 上的 www 文件夹加载资源 (file://)

    我有一个混合应用程序 可以使用 Cordova 3 7 和 jQuery Mobile 1 3 2 在 iOS 7 上完美运行 我正在 iOS 8 上测试它 但它坏了 我使用绝对路径请求应用程序的每个页面 视图 使用file 协议 例如 f
  • 如何在 Windows 上模拟 iPad 浏览器

    如何在windows下模拟iPad浏览器 我知道用户代理字符串 并且我知道浏览器宽度 高度 屏幕宽度 高度 我相信 如果您模拟 iPad 的用户代理并适当设置窗口大小 您就可以使用 Safari Windows 从 apple com 下载
  • 如何在 UML 属性中指定枚举文字作为默认值?

    我目前使用 EMF UML Implementation 进行一些模型转换 在我的模型转换中 我创建了一个具有一些属性的 uml 类 这些属性是我也创建的枚举类型 某些属性应该获得默认值 默认值应该是枚举文字 现在的问题是 如何将枚举文字获
  • 如何在 jQuery 中将值从一个页面传递到另一个页面

    我有两个 jQuery 页面 Page1 和 Page2 并且我能够在 Page1 中获取输入 The somval 1000 第 1 页用户输入某个值 我已经存储了该值 var val somval 现在在第二页中 我需要获取第1页中so
  • 使用 keycloak 进行 Spring Boot 测试

    我正在尝试运行简单的单元测试 Keycloak 以正确的方式配置 我测试了它 我的 mvc 应用程序正在连接并且用户已通过身份验证 但现在我尝试测试我的控制器 即使 我使用 spring slices keycloak 适配器被调用并给我错