我正在开发一个Java EE Web应用程序,它将利用Keycloak作为身份和身份验证提供程序。此外,出于各种目的,我正在项目中使用Apache DeltaSpike。我想利用DeltaSpike的安全模块来注释方法,指示用户必须登录才能访问它们。如果此检查失败,我想将用户自动重定向到Keycloak登录页面。但是,我还没有找到一种方法来实现这一目标。
相关的y web.xml文件如下所示,表明我已将Keycloak配置为我的应用程序的安全性机制:
<login-config>
<auth-method>KEYCLOAK</auth-method>
<realm-name>KEYCLOAK</realm-name>
</login-config>
我已经实现了一个AccessDecisionVoter
类似如下的代码:
public class LoginRequiredAccessDecisionVoter extends AbstractAccessDecisionVoter {
@Inject
private KeycloakSecurityContext securityContext = null;
/*
* (non-Javadoc)
*
* @see
* org.apache.deltaspike.security.api.authorization.AbstractAccessDecisionVoter#
* checkPermission(org.apache.deltaspike.security.api.authorization.
* AccessDecisionVoterContext, java.util.Set)
*/
@Override
protected void checkPermission(AccessDecisionVoterContext accessDecisionVoterContext,
Set<SecurityViolation> violations) {
if (securityContext == null) {
violations.add(new LoginRequiredSecurityViolation());
}
}
}
然后,我将DeltaSpike的类型感知JSF导航配置为使用此类保护。我用浏览器访问了受保护的资源,发现我AccessDecisionVoter
被触发了。我看到该checkPermission
方法被调用ErrorViewAwareAccessDeniedException
并被抛出。因此,我实现了一个ExceptionHandler
捕获异常并将用户重定向到Keycloak登录页面。看起来像这样:
@ExceptionHandler
public class SecurityExceptionHandler {
@Inject
private HttpServletRequest servletRequest = null;
@Inject
@DeltaSpike
private HttpServletResponse servletResponse = null;
/**
* Handles {@link ErrorViewAwareAccessDeniedException} exceptions
*
* @param event The exception event
*/
public void handleErrorViewAwareAccessDeniedException(
@Handles ExceptionEvent<ErrorViewAwareAccessDeniedException> event) {
Set<SecurityViolation> violations = event.getException().getViolations();
for (SecurityViolation v : violations) {
if (v instanceof LoginRequiredSecurityViolation) {
try {
this.servletRequest.authenticate(this.servletResponse);
event.handled();
} catch (IOException | ServletException e) {
}
}
}
}
}
我的想法是该servletRequest.authenticate()
呼叫将启动重定向到Keycloak登录页面的重定向。但是,事实并非如此。相反,我得到一个ServletException
/NullPointerException
抛出。
是否有更好的方法以编程方式将用户重定向到Keycloak?是否有异常或可以引发触发重定向的东西?有什么想法吗?
更新:这是我收到的异常:
11:17:54,256 ERROR [io.undertow.request] (default task-1) UT005023: Exception handling request to /tests/loginTest.xhtml: javax.servlet.ServletException
at javax.faces.webapp.FacesServlet.executeLifecyle(FacesServlet.java:725)
at javax.faces.webapp.FacesServlet.service(FacesServlet.java:451)
at io.undertow.servlet.handlers.ServletHandler.handleRequest(ServletHandler.java:74)
at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:129)
at org.apache.deltaspike.servlet.impl.event.EventBridgeFilter.doFilter(EventBridgeFilter.java:59)
at io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:61)
at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131)
at org.apache.deltaspike.servlet.impl.produce.RequestResponseHolderFilter.doFilter(RequestResponseHolderFilter.java:63)
at io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:61)
at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131)
at io.opentracing.contrib.jaxrs2.server.SpanFinishingFilter.doFilter(SpanFinishingFilter.java:55)
at io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:61)
at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131)
at io.undertow.servlet.handlers.FilterHandler.handleRequest(FilterHandler.java:84)
at io.undertow.servlet.handlers.security.ServletSecurityRoleHandler.handleRequest(ServletSecurityRoleHandler.java:62)
at io.undertow.servlet.handlers.ServletChain$1.handleRequest(ServletChain.java:68)
at io.undertow.servlet.handlers.ServletDispatchingHandler.handleRequest(ServletDispatchingHandler.java:36)
at org.wildfly.elytron.web.undertow.server.ElytronRunAsHandler.lambda$handleRequest$1(ElytronRunAsHandler.java:68)
at org.wildfly.security.auth.server.FlexibleIdentityAssociation.runAsFunctionEx(FlexibleIdentityAssociation.java:103)
at org.wildfly.security.auth.server.Scoped.runAsFunctionEx(Scoped.java:161)
at org.wildfly.security.auth.server.Scoped.runAs(Scoped.java:73)
at org.wildfly.elytron.web.undertow.server.ElytronRunAsHandler.handleRequest(ElytronRunAsHandler.java:67)
at io.undertow.servlet.handlers.security.SSLInformationAssociationHandler.handleRequest(SSLInformationAssociationHandler.java:132)
at io.undertow.servlet.handlers.security.ServletAuthenticationCallHandler.handleRequest(ServletAuthenticationCallHandler.java:57)
at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
at io.undertow.security.handlers.AuthenticationConstraintHandler.handleRequest(AuthenticationConstraintHandler.java:53)
at io.undertow.security.handlers.AbstractConfidentialityHandler.handleRequest(AbstractConfidentialityHandler.java:46)
at io.undertow.servlet.handlers.security.ServletConfidentialityConstraintHandler.handleRequest(ServletConfidentialityConstraintHandler.java:64)
at io.undertow.servlet.handlers.security.ServletSecurityConstraintHandler.handleRequest(ServletSecurityConstraintHandler.java:59)
at io.undertow.security.handlers.AbstractSecurityContextAssociationHandler.handleRequest(AbstractSecurityContextAssociationHandler.java:43)
at org.wildfly.elytron.web.undertow.server.servlet.CleanUpHandler.handleRequest(CleanUpHandler.java:38)
at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
at org.wildfly.extension.undertow.security.jacc.JACCContextIdHandler.handleRequest(JACCContextIdHandler.java:61)
at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
at org.wildfly.extension.undertow.deployment.GlobalRequestControllerHandler.handleRequest(GlobalRequestControllerHandler.java:68)
at org.keycloak.adapters.elytron.KeycloakServletExtension.lambda$null$0(KeycloakServletExtension.java:39)
at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
at io.undertow.servlet.handlers.ServletInitialHandler.handleFirstRequest(ServletInitialHandler.java:292)
at io.undertow.servlet.handlers.ServletInitialHandler.access$100(ServletInitialHandler.java:81)
at io.undertow.servlet.handlers.ServletInitialHandler$2.call(ServletInitialHandler.java:138)
at io.undertow.servlet.handlers.ServletInitialHandler$2.call(ServletInitialHandler.java:135)
at io.undertow.servlet.core.ServletRequestContextThreadSetupAction$1.call(ServletRequestContextThreadSetupAction.java:48)
at io.undertow.servlet.core.ContextClassLoaderSetupAction$1.call(ContextClassLoaderSetupAction.java:43)
at org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1502)
at org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1502)
at org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1502)
at org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1502)
at org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1502)
at io.undertow.servlet.handlers.ServletInitialHandler.dispatchRequest(ServletInitialHandler.java:272)
at io.undertow.servlet.handlers.ServletInitialHandler.access$000(ServletInitialHandler.java:81)
at io.undertow.servlet.handlers.ServletInitialHandler$1.handleRequest(ServletInitialHandler.java:104)
at io.undertow.server.Connectors.executeRootHandler(Connectors.java:364)
at io.undertow.server.HttpServerExchange$1.run(HttpServerExchange.java:830)
at org.jboss.threads.ContextClassLoaderSavingRunnable.run(ContextClassLoaderSavingRunnable.java:35)
at org.jboss.threads.EnhancedQueueExecutor.safeRun(EnhancedQueueExecutor.java:1982)
at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.doRunTask(EnhancedQueueExecutor.java:1486)
at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1377)
at java.lang.Thread.run(Thread.java:748)
Caused by: java.lang.NullPointerException
at io.undertow.servlet.spec.HttpServletRequestImpl.getRequestDispatcher(HttpServletRequestImpl.java:957)
at com.sun.faces.context.ExternalContextImpl.dispatch(ExternalContextImpl.java:692)
at javax.faces.context.ExternalContextWrapper.dispatch(ExternalContextWrapper.java:91)
at com.sun.faces.application.view.JspViewHandlingStrategy.executePageToBuildView(JspViewHandlingStrategy.java:356)
at com.sun.faces.application.view.JspViewHandlingStrategy.buildView(JspViewHandlingStrategy.java:106)
at com.sun.faces.lifecycle.RenderResponsePhase.execute(RenderResponsePhase.java:78)
at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:76)
at com.sun.faces.lifecycle.LifecycleImpl.render(LifecycleImpl.java:199)
at org.apache.deltaspike.jsf.impl.listener.request.DeltaSpikeLifecycleWrapper.render(DeltaSpikeLifecycleWrapper.java:111)
at javax.faces.lifecycle.LifecycleWrapper.render(LifecycleWrapper.java:88)
at org.apache.deltaspike.jsf.impl.listener.request.JsfClientWindowAwareLifecycleWrapper.render(JsfClientWindowAwareLifecycleWrapper.java:160)
at javax.faces.webapp.FacesServlet.executeLifecyle(FacesServlet.java:708)
... 57 more
解决了!它所需要的只是在通话FacesContext#responseComplete()
之前authenticate
打电话。因此,代码的相关部分现在看起来像这样(也做了一些清理):
FacesContext ctx = FacesContext.getCurrentInstance();
HttpServletRequest req = (HttpServletRequest) ctx.getExternalContext().getRequest();
HttpServletResponse resp = (HttpServletResponse) ctx.getExternalContext().getResponse();
ctx.responseComplete();
req.authenticate(resp);
event.handled();
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句