java - Undertow servlet 处理程序丢失了由 keycloak 管理的已通过身份验证且有效的 SecurityContext

标签 java authentication servlets keycloak undertow

我正在尝试在使用 undertow、jersey 和 CDI 设置的休息端点上启用基于角色的访问控制。我按如下方式初始化 servlet 部署:

DeploymentInfo servletBuilder = Servlets.deployment()
    .setClassLoader(Main.class.getClassLoader())
    .setContextPath("/rest")
    .setDeploymentName("sv.war")
    .addListeners(listener(Listener.class))
    .setLoginConfig(new LoginConfig("KEYCLOAK", "some-realm"))
    .setAuthorizationManager(auth) // my dummy for testing
    .addServlets(servlet("jerseyServlet", ServletContainer.class)
        .setLoadOnStartup(1)
        .addInitParam("javax.ws.rs.Application", SystemViewApplication.class.getName())
        .addMapping("/api/*"));

我基于this example code启用了kecloak身份验证.

所以,我的服务器启动为:

DeploymentManager manager = Servlets.defaultContainer().addDeployment(servletBuilder);
manager.deploy();

PathHandler path = Handlers.path(Handlers.resource(staticResources).setDirectoryListingEnabled(false).setWelcomeFiles("index.html"))
    .addPrefixPath("/rest", manager.start());
Undertow server = Undertow.builder()
    .addHttpListener(8087, "localhost")
    .setHandler(sessionHandling(addSecurity(exchange -> {
      final SecurityContext context = exchange.getSecurityContext();
      if (!context.isAuthenticated()) {
        exchange.endExchange();
        return;
      }
      log.info("Authenticated: {} {} {}", context.getMechanismName(), context.getAuthenticatedAccount().getPrincipal().getName(), context.getAuthenticatedAccount().getRoles());
      // propagate the request
      path.handleRequest(exchange);
    })))
    .build();
server.start();

其中两个方法 sessionHandling()addSecurity() 是从我上面链接的示例中提取的。

身份验证有效,我被迫登录,并且 Authenticated: .. 日志行打印出正确的详细信息。但是,一旦遇到 servlet 处理,安全上下文(和帐户)就会丢失。我跟踪了这​​个调用,我可以看到在路径上的某个时刻,它被具有空帐户的全新 SecurityContext 所取代。

现在我的问题 - 是否有一些我缺少的身份验证机制可以在 keycloak 身份验证后传播状态,或者我可以修复 undertow 代码并在 SecurityContext 中(如果传入上下文)已经正确验证,接受该状态并继续吗? (后者似乎不对,我猜这是因为 servlet 部署的身份验证可能不同?)如果是这样,有什么方法可以连接 servlet 部署以查看 keycloak 身份验证是否已经发生?

最佳答案

如果有人来这里寻找如何使用 keycloak 正确验证 servlet 并使用基于角色的身份验证,这对我有用(注意,这对我有用,不需要任何 xml 文件,纯粹是通过注释。

首先在 servlet 应用程序中(无论您在何处扩展 ResourceConfig)register() RolesAllowedDynamicFeature.class

同时在 keycloak.json 中启用 "use-resource-role-mappings": true

接下来,使用初始安全包装器实例化 servlet 部署:

DeploymentInfo servletBuilder = Servlets.deployment()
    .setClassLoader(Main.class.getClassLoader())
    .setContextPath("/")
    .setDeploymentName("sv.war")
    .addListeners(listener(Listener.class))
    .setIdentityManager(idm)
    .setSessionManagerFactory(new InMemorySessionManagerFactory())
    .setInitialSecurityWrapper(handler -> sessionHandling(addSecurity(handler)))
    .setResourceManager(staticResources)
    .addWelcomePage("index.html")
    .addServlets(servlet("jerseyServlet", ServletContainer.class)
        .setLoadOnStartup(1)
        .addInitParam("javax.ws.rs.Application", SystemViewApplication.class.getName())
        .addMapping("/api/*"));


DeploymentManager manager = Servlets.defaultContainer().addDeployment(servletBuilder);
manager.deploy();

Undertow server = Undertow.builder()
    .addHttpListener(8087, "localhost")
    .setHandler(Handlers.path(manager.start()))
    .build();
server.start();

其中 sessionHandling(addSecurity(handler)) 基本上是链接的 github 存储库中的代码。

现在通过 keycloak 进行身份验证将起作用,并且基于角色的身份验证也将起作用,因此,例如,如果您有一个 CDI 注入(inject)的休息端点,例如:

@RolesAllowed({"admin", "guest"})
@GET
@Path("/{id}")
public Response findById(@PathParam("id") @NotNull Integer id){
  // some method
}

只要在 keycloak 中配置了角色,它就应该可以工作。

关于java - Undertow servlet 处理程序丢失了由 keycloak 管理的已通过身份验证且有效的 SecurityContext,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44948550/

相关文章:

java - 从不同目录运行 jar 找不到所需的依赖项

Java:Swing 中的 HTML/CSS,显示内联不起作用

java - 在 NxN 网格中查找所有路径的算法

android - 基于登录的不同内容

java - 使用 pack() 将 JFrame 缩放到适当大小时出现问题

node.js - 了解 Passport 序列化反序列化

php - 如何在PHP中登录后转到同一页面

java - 如何知道从哪个jsp页面调用servlet

java - 新上传的文件和 Tomcat?

java - Servlet 只读取 Excel 文件的前两行