keycloak~自定义登出接口

Posted 敢于对过去告一个段落,才有信心掀开新的篇章!

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了keycloak~自定义登出接口相关的知识,希望对你有一定的参考价值。

keycloak提供了登出的接口,不过它是一个post方法,需要你根据client_id,client_secret及refresh_token进行登出操作的,有时不太灵活,所以我又自己封装了一下,通过客户端浏览器上存储的session_id进行会话登出。

kc提供的logout

  • api:host/auth/realms/fabao/protocol/openid-connect/logout
  • 请求方法:POST
  • 请求类型:x-www-form-urlencoded
  • 参数:
refresh_token:xxx
client_id:xxx
client_secret:xxx

对remove-sessions登出的封装

  // keycloak会话登出逻辑
  @GET
  @NoCache
  @Path("remove-sessions")
  public Response deleteSession(@Context HttpRequest request,
                                @QueryParam("redirect_uri") String redirectUri) 
    return removeSession(TokenUtils.getKeycloakSessionIdFromCookies(request), redirectUri);
  
private Response removeSession(String sessionId, String redirectUri) 
    if (redirectUri == null || redirectUri.trim().length() == 0) 
      log.info("redirect_uri不正确");
      return Response.status(400).entity("redirect_uri不正确").type(MediaType.TEXT_HTML).build();
    

    TokenUtils.removeSession(session, sessionId);//清除会话
    return Response.status(302)
        .location(HttpUtils.formatUrl(HttpUtils.removeUrlSpaceParams(redirectUri)))
        .cookie(CookieUtils.addCookie(TokenUtils.KC_EXIT_LEGACY, "1"))
        .header(HttpHeaders.SET_COOKIE,
            CookieUtils.cookieString(TokenUtils.KC_EXIT, "1", "/", null, null, -1, true, true,
                ServerCookie.SameSiteAttributeValue.NONE))
        .build();

public static void removeSession(KeycloakSession session, String sessionId) 
    try 
      if (sessionId != null) 
        RealmModel realmModel = session.getContext().getRealm();
        log.debugf("will remove sessionId:%s", sessionId);
        UserSessionModel userSession = session.sessions().getUserSession(session.getContext().getRealm(), sessionId);
        if (userSession != null) 
          String path = String.format("/auth/realms/%s/", session.getContext().getRealm().getId());
          CookieUtils.expireCookie(session.getContext().getRealm(), TokenUtils.KEYCLOAK_SESSION,
              path, null, false,
              session.getContext().getConnection(), ServerCookie.SameSiteAttributeValue.NONE);
          CookieUtils.expireCookie(session.getContext().getRealm(), TokenUtils.KEYCLOAK_SESSION_LEGACY, path, null,
              false,
              session.getContext().getConnection(), null);
          CookieUtils.expireCookie(session.getContext().getRealm(), TokenUtils.AUTH_SESSION_ID,
              path, null, false,
              session.getContext().getConnection(), ServerCookie.SameSiteAttributeValue.NONE);
          CookieUtils.expireCookie(session.getContext().getRealm(), TokenUtils.AUTH_SESSION_ID_LEGACY, path, null,
              false,
              session.getContext().getConnection(), null);

          AuthenticationManager.backchannelLogout(session,
              realmModel,
              userSession,
              session.getContext().getUri(),
              session.getContext().getConnection(),
              session.getContext().getRequestHeaders(),
              true);

        
      
     catch (Exception ex) 
      log.error(ex);
    

  

通过上面的封装,我们在其它对应到keycloak的客户端,直接在浏览器上输入/auth/realms/fabao/sms/remove-sessions?redirect_uri=your-site,就可以把keycloak在当前浏览器里的会话登出了,并重定向到自己的you-site网站。

部署keycloak自定义spi部署

【中文标题】部署keycloak自定义spi部署【英文标题】:Deploy keycloak custom spi deployment 【发布时间】:2020-01-08 17:36:31 【问题描述】:

我尝试创建一个自定义 spi,在我的 keycloak 项目中,遵循基本的 keycloack 结构,我添加自定义提供程序接口,扩展提供程序,自定义提供程序工厂并为他们实现自定义 spi,如 keycloak 文档所述,他们在他们的源代码,之后我为我的提供者和提供者工厂创建了一个自定义实现,我按照文档所述在 META-INF/services 中创建文件,并且我正在使用 ear aproach 进行部署,就像在 beercloak 示例中一样,但是当我尝试使用抛出了代码中的提供程序空指针异常,这仅在我尝试添加自定义 spi 时发生,如果我正在实现具有现有 keycloak spi 的提供程序,它也可以工作,如果我使用模块方法,它也可以工作,我在哪里创建一个带有 jboss-cli 的新模块,但这种方法似乎很难维护,任何人都知道为什么会发生这种情况,我该如何解决它或者最好的方法是什么,谢谢。

08:43:48,264 WARN [org.keycloak.services](默认任务 1)KC-SERVICES0013:身份验证失败:java.lang.NullPointerException 在 sso.authentication.forms.RegistrationProfile.validate(RegistrationProfile.java:55) 在 org.keycloak.authentication.FormAuthenticationFlow.processAction(FormAuthenticationFlow.java:214) 在 org.keycloak.authentication.DefaultAuthenticationFlow.processAction(DefaultAuthenticationFlow.java:99) 在 org.keycloak.authentication.AuthenticationProcessor.authenticationAction(AuthenticationProcessor.java:873) 在 org.keycloak.services.resources.LoginActionsService.processFlow(LoginActionsService.java:296) 在 org.keycloak.services.resources.LoginActionsService.processRegistration(LoginActionsService.java:631) 在 org.keycloak.services.resources.LoginActionsService.registerRequest(LoginActionsService.java:685) 在 org.keycloak.services.resources.LoginActionsService.processRegister(LoginActionsService.java:665) 在 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.jboss.resteasy.core.MethodInjectorImpl.invoke(MethodInjectorImpl.java:138) 在 org.jboss.resteasy.core.ResourceMethodInvoker.internalInvokeOnTarget(ResourceMethodInvoker.java:517) 在 org.jboss.resteasy.core.ResourceMethodInvoker.invokeOnTargetAfterFilter(ResourceMethodInvoker.java:406) 在 org.jboss.resteasy.core.ResourceMethodInvoker.lambda$invokeOnTarget$0(ResourceMethodInvoker.java:370) 在 org.jboss.resteasy.core.interception.PreMatchContainerRequestContext.filter(PreMatchContainerRequestContext.java:355) 在 org.jboss.resteasy.core.ResourceMethodInvoker.invokeOnTarget(ResourceMethodInvoker.java:372) 在 org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:344) 在 org.jboss.resteasy.core.ResourceLocatorInvoker.invokeOnTargetObject(ResourceLocatorInvoker.java:137) 在 org.jboss.resteasy.core.ResourceLocatorInvoker.invoke(ResourceLocatorInvoker.java:100) 在 org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:440) 在 org.jboss.resteasy.core.SynchronousDispatcher.lambda$invoke$4(SynchronousDispatcher.java:229) 在 org.jboss.resteasy.core.SynchronousDispatcher.lambda$preprocess$0(SynchronousDispatcher.java:135) 在 org.jboss.resteasy.core.interception.PreMatchContainerRequestContext.filter(PreMatchContainerRequestContext.java:355) 在 org.jboss.resteasy.core.SynchronousDispatcher.preprocess(SynchronousDispatcher.java:138) 在 org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:215) 在 org.jboss.resteasy.plugins.server.servlet.ServletContainerDispatcher.service(ServletContainerDispatcher.java:227) 在 org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher.service(HttpServletDispatcher.java:56) 在 org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher.service(HttpServletDispatcher.java:51) 在 javax.servlet.http.HttpServlet.service(HttpServlet.java:791) 在 io.undertow.servlet.handlers.ServletHandler.handleRequest(ServletHandler.java:74) 在 io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:129) 在 org.keycloak.services.filters.KeycloakSessionServletFilter.doFilter(KeycloakSessionServletFilter.java:90) 在 io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:61) 在 io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131) 在 io.undertow.servlet.handlers.FilterHandler.handleRequest(FilterHandler.java:84) 在 io.undertow.servlet.handlers.security.ServletSecurityRoleHandler.handleRequest(ServletSecurityRoleHandler.java:62) 在 io.undertow.servlet.handlers.ServletChain$1.handleRequest(ServletChain.java:68) 在 io.undertow.servlet.handlers.ServletDispatchingHandler.handleRequest(ServletDispatchingHandler.java:36) 在 org.wildfly.extension.undertow.security.SecurityContextAssociationHandler.handleRequest(SecurityContextAssociationHandler.java:78) 在 io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43) 在 io.undertow.servlet.handlers.security.SSLInformationAssociationHandler.handleRequest(SSLInformationAssociationHandler.java:132) 在 io.undertow.servlet.handlers.security.ServletAuthenticationCallHandler.handleRequest(ServletAuthenticationCallHandler.java:57) 在 io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43) 在 io.undertow.security.handlers.AbstractConfidentialityHandler.handleRequest(AbstractConfidentialityHandler.java:46) 在 io.undertow.servlet.handlers.security.ServletConfidentialityConstraintHandler.handleRequest(ServletConfidentialityConstraintHandler.java:64) 在 io.undertow.security.handlers.AuthenticationMechanismsHandler.handleRequest(AuthenticationMechanismsHandler.java:60) 在 io.undertow.servlet.handlers.security.CachedAuthenticatedSessionHandler.handleRequest(CachedAuthenticatedSessionHandler.java:77) 在 io.undertow.security.handlers.NotificationReceiverHandler.handleRequest(NotificationReceiverHandler.java:50) 在 io.undertow.security.handlers.AbstractSecurityContextAssociationHandler.handleRequest(AbstractSecurityContextAssociationHandler.java:43) 在 io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43) 在 org.wildfly.extension.undertow.security.jacc.JACCContextIdHandler.handleRequest(JACCContextIdHandler.java:61) 在 io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43) 在 org.wildfly.extension.undertow.deployment.GlobalRequestControllerHandler.handleRequest(GlobalRequestControllerHandler.java:68) 在 io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43) 在 io.undertow.servlet.handlers.ServletInitialHandler.handleFirstRequest(ServletInitialHandler.java:292) 在 io.undertow.servlet.handlers.ServletInitialHandler.access 100 美元(ServletInitialHandler.java:81) 在 io.undertow.servlet.handlers.ServletInitialHandler$2.call(ServletInitialHandler.java:138) 在 io.undertow.servlet.handlers.ServletInitialHandler$2.call(ServletInitialHandler.java:135) 在 io.undertow.servlet.core.ServletRequestContextThreadSetupAction$1.call(ServletRequestContextThreadSetupAction.java:48) 在 io.undertow.servlet.core.ContextClassLoaderSetupAction$1.call(ContextClassLoaderSetupAction.java:43) 在 org.wildfly.extension.undertow.security.SecurityContextThreadSetupAction.lambda$create$0(SecurityContextThreadSetupAction.java:105) 在 org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1502) 在 org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1502) 在 org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1502) 在 org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1502) 在 io.undertow.servlet.handlers.ServletInitialHandler.dispatchRequest(ServletInitialHandler.java:272) 在 io.undertow.servlet.handlers.ServletInitialHandler.access$000(ServletInitialHandler.java:81) 在 io.undertow.servlet.handlers.ServletInitialHandler$1.handleRequest(ServletInitialHandler.java:104) 在 io.undertow.server.Connectors.executeRootHandler(Connectors.java:364) 在 io.undertow.server.HttpServerExchange$1.run(HttpServerExchange.java:830) 在 org.jboss.threads.ContextClassLoaderSavingRunnable.run(ContextClassLoaderSavingRunnable.java:35) 在 org.jboss.threads.EnhancedQueueExecutor.safeRun(EnhancedQueueExecutor.java:1982) 在 org.jboss.threads.EnhancedQueueExecutor$ThreadBody.doRunTask(EnhancedQueueExecutor.java:1486) 在 org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1377) 在 java.lang.Thread.run(Thread.java:748)

【问题讨论】:

您能否使用收到的错误日志更新您的问题。这样有助于人们了解您在哪里犯了错误或指出出了什么问题。 【参考方案1】:

如果您仔细查看 $JBOSS_HOME/standalone/configuration/standalon-ha.xml$JBOSS_HOME/standalone/configuration/standalon.xml 文件,您会在某处注意到这些行:

<providers>
    <provider>classpath:$jboss.home.dir/providers/*</provider>
</providers>

这意味着,您需要将已编译的文件放在根目录中的文件夹 $jboss.home.dir/providers/ 中,然后才能注册它们的 SPI

这样做基本上与在此处添加带有模块路径的新行相同,例如

<providers>
    <provider>classpath:$jboss.home.dir/providers/*</provider>
    <provider>module:com/selast/keycloak-provider/*</provider>
</providers>

希望它对某人有所帮助,因为我已经花了一天的时间?。

【讨论】:

以上是关于keycloak~自定义登出接口的主要内容,如果未能解决你的问题,请参考以下文章

Keycloak 创建和修改自定义用户信息

部署keycloak自定义spi部署

为Keycloak添加自定义API

如何在 NodeJS 中自定义 keycloak 错误消息

Keycloak - 自定义 SPI 未出现在列表中

如何通过employeeNumber或自定义属性搜索keycloak用户?