簡體   English   中英

如何在 Keycloak 和 Apache Deltaspike 中重定向到登錄頁面?

[英]How to Redirect to login page in Keycloak and Apache Deltaspike?

我正在開發一個 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 導航配置為使用此 class 保護。 我用瀏覽器訪問了一個安全資源,發現我的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

解決了! 它所需要的只是在authenticate調用之前調用FacesContext#responseComplete() 所以,相關的代碼部分現在看起來像這樣(也清理了一點):

FacesContext ctx = FacesContext.getCurrentInstance();
HttpServletRequest req = (HttpServletRequest) ctx.getExternalContext().getRequest();
HttpServletResponse resp = (HttpServletResponse) ctx.getExternalContext().getResponse();

ctx.responseComplete();
req.authenticate(resp);

event.handled();

如果我嘗試創建一個 ExceptionHandler 並在注入的servletRequest上調用authenticate() ,我會得到以下異常:

java.lang.IllegalStateException: Attempt to access the request/response without an active HTTP request
    org.apache.deltaspike.servlet.impl.produce.RequestResponseHolder.get(RequestResponseHolder.java:105)
    org.apache.deltaspike.servlet.impl.produce.ServletObjectProducer.getHttpServletResponse(ServletObjectProducer.java:95)
    sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    java.lang.reflect.Method.invoke(Method.java:498)
    org.apache.webbeans.inject.InjectableMethod.doInjection(InjectableMethod.java:155)
    org.apache.webbeans.portable.ProducerMethodProducer.produce(ProducerMethodProducer.java:89)
    org.apache.webbeans.portable.AbstractProducer.produce(AbstractProducer.java:134)
    org.apache.webbeans.component.AbstractOwbBean.create(AbstractOwbBean.java:122)
    org.apache.webbeans.component.ProducerMethodBean.create(ProducerMethodBean.java:95)
    org.apache.webbeans.context.creational.BeanInstanceBag.create(BeanInstanceBag.java:76)
    org.apache.webbeans.context.AbstractContext.getInstance(AbstractContext.java:159)
    org.apache.webbeans.context.AbstractContext.get(AbstractContext.java:125)
    org.apache.webbeans.intercept.NormalScopedBeanInterceptorHandler.getContextualInstance(NormalScopedBeanInterceptorHandler.java:101)
    org.apache.webbeans.intercept.RequestScopedBeanInterceptorHandler.getContextualInstance(RequestScopedBeanInterceptorHandler.java:76)
    org.apache.webbeans.intercept.NormalScopedBeanInterceptorHandler.get(NormalScopedBeanInterceptorHandler.java:71)
    org.apache.webbeans.custom.servlet.http.HttpServletResponse$$OwbNormalScopeProxy0.isCommitted(javax/servlet/http/HttpServletResponse.java)
    org.apache.catalina.connector.Request.authenticate(Request.java:2742)
    org.apache.catalina.connector.RequestFacade.authenticate(RequestFacade.java:1081)
    org.apache.webbeans.custom.servlet.http.HttpServletRequest$$OwbNormalScopeProxy0.authenticate(javax/servlet/http/HttpServletRequest.java)

facesContext獲取request / response對象時,我沒有收到此異常:

FacesContext ctx = FacesContext.getCurrentInstance();
ExternalContext eCtx = ctx.getExternalContext();
HttpServletResponse response = (HttpServletResponse) eCtx.getResponse();
HttpServletRequest request = (HttpServletRequest) eCtx.getRequest();
request.authenticate(response);

我對 deltaspike 不是很熟悉,也沒有在我的測試中添加 keycloak。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM