You need to enable JavaScript to run this app. Keycloak SAML搭配Tomcat适配器实现SSO时POST流程无法获取SAML Assertion
优惠活动
大模型
产品
解决方案
定价
更多

Keycloak SAML搭配Tomcat适配器实现SSO时POST流程无法获取SAML Assertion

问题根因与对应解决方案

核心错误1:业务Servlet占用了适配器默认的SAML断言接收端点

Keycloak Tomcat SAML适配器默认将 /saml 作为SAML Assertion的接收(ACS)端点,IdP(Keycloak)发回的POST请求会优先被适配器拦截处理:完成签名校验、断言解析、本地会话建立后,直接302跳转到用户最初访问的受保护资源,全程不会转发到你自定义的业务Servlet。
你在访问日志中看到的 POST /simple/saml 返回302,就是适配器处理完断言后的跳转动作,并非你的AuthServlet执行结果,因此永远不会输出POSTing日志。

修正方式:

  • 将AuthServlet的映射路径从/saml改为非保留路径,例如/userInfo:
<servlet-mapping>
    <servlet-name>AuthServlet</servlet-name>
    <url-pattern>/userInfo</url-pattern>
</servlet-mapping>

错误2:login-config配置与Keycloak SAML适配器不兼容

你当前配置的<auth-method>BASIC</auth-method>是Servlet容器原生的基础认证配置,完全无法触发Keycloak SAML适配器的认证逻辑。

修正方式:
将login-config替换为SAML适配器专属的认证方法,realm-name需和适配器配置文件中定义的领域名称一致:

<login-config>
    <auth-method>KEYCLOAK_SAML</auth-method>
    <realm-name>你的Keycloak SAML领域名称</realm-name>
</login-config>

错误3:未完成适配器的必备组件配置

Tomcat版Keycloak SAML适配器无法仅靠web.xml生效,还需要两个核心配置:

  • 在应用的WEB-INF/目录下添加适配器配置文件keycloak-saml.xml,填写Keycloak领域元数据、客户端ID、ACS地址、断言签名校验密钥等信息
  • 给Web应用配置SAML认证Valve,可在应用的META-INF/context.xml中添加:
<Valve className="org.keycloak.adapters.saml.tomcat.SamlAuthenticator"/>

同时需将对应版本的Keycloak Tomcat SAML适配器jar包放到Tomcat的lib目录下(不可仅放到应用的WEB-INF/lib,Valve类需要被容器类加载器加载)。


错误4:ACS端点未配置为公开访问

你当前的安全约束/*会将/saml端点标记为受保护资源,未建立会话的请求(也就是Keycloak发回的SAML POST请求)可能被拦截触发登录跳转死循环。

修正方式:
在原有安全约束前添加单独的规则,将/saml设为公开:

<security-constraint>
    <web-resource-collection>
        <web-resource-name>SAML ACS端点</web-resource-name>
        <url-pattern>/saml</url-pattern>
    </web-resource-collection>
    <!-- 无auth-constraint即代表所有用户可访问 -->
</security-constraint>

错误5:SamlPrincipal获取逻辑不规范

SAML断言解析完成后会直接绑定到已建立的会话中,不需要在ACS端点处理。修改Servlet路径后,可在业务路径的请求中直接获取,建议增加类型判断避免异常:

public class AuthServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        if (request.getUserPrincipal() instanceof SamlPrincipal) {
            SamlPrincipal samlPrincipal = (SamlPrincipal) request.getUserPrincipal();
            samlPrincipal.getAttributeNames().asIterator().forEachRemaining(attr -> {
                String value = samlPrincipal.getAttribute(attr);
                getServletContext().log("key: " + attr + " value: " + value);
            });
        }
        // 后续业务逻辑...
    }
}

内容的提问来源于stack exchange,提问作者Dennis Jansky

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.30 02:57:02