我正在尝试在使用 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/