我正在尝试在我使用underow,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认证后传播状态,或者我可以修复底部代码和SecurityContext
,如果传入的上下文已经正确认证,接受该状态并移动上? (后者似乎不正确,我猜它是因为可能是servlet部署的不同身份验证?)如果是这样,有没有办法连接servlet部署以查看keycloak身份验证已经发生?
任何人都来这里看看如何使用keycloak正确验证servlet并使用基于角色的身份验证,这对我有用(注意,这对我有用,不需要任何xml文件,纯粹带有注释。
首先在servlet应用程序(无论你在哪里扩展ResourceConfig
)register()
RolesAllowedDynamicFeature.class
。
同时在"use-resource-role-mappings": true
启用keycloak.json
。
接下来,使用初始安全包装器实例化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 repo的代码。
现在通过keycloak进行身份验证将起作用,并且基于角色的身份验证也可以正常工作,例如,如果您有一个CDI注入的休息终点,例如:
@RolesAllowed({"admin", "guest"})
@GET
@Path("/{id}")
public Response findById(@PathParam("id") @NotNull Integer id){
// some method
}
只要角色在keycloak中配置,它就应该有效。