JBoss下Java8无Spring应用:SAML认证后@RolesAllowed在EJB中失效
核心原因
@Stateless EJB的安全验证依赖JBoss的JAAS上下文,而Keycloak Servlet Filter适配器默认仅填充Web层(HttpServletRequest)的角色,未将角色同步到EJB使用的JAAS Subject中;旧LDAP配置可能残留了EJB专属的安全域配置,导致EJB仍在使用旧的认证源。
具体解决步骤
统一Web与EJB的安全域
打开JBoss的standalone.xml(或domain.xml),找到<security-domains>节点,确保Keycloak配置的安全域(通常名为keycloak)被Web和EJB同时使用。修改应用的jboss-web.xml和jboss-ejb3.xml,指定<security-domain>keycloak</security-domain>,替代旧的LDAP安全域。配置Keycloak角色映射到JAAS上下文
在应用的keycloak.json中添加角色映射规则,确保IdP返回的角色被正确注入到JAAS Subject:{ "realm": "your-realm", "auth-server-url": "https://your-keycloak-url/auth", "resource": "your-client-id", "role-mappings": { "user_role": ["user_role"] }, "principal-attribute": "preferred_username", "use-resource-role-mappings": true }同时在
web.xml中声明安全角色,与EJB的@RolesAllowed对应:<security-role> <role-name>user_role</role-name> </security-role>验证JAAS Subject的角色
在Servlet中添加调试代码,确认JAAS上下文是否包含目标角色:import org.jboss.security.SecurityContextAssociation; import javax.security.auth.Subject; import java.util.Set; // 在认证后的Servlet方法中执行 Subject subject = SecurityContextAssociation.getSubject(); Set<Principal> principals = subject.getPrincipals(); principals.forEach(p -> System.out.println("Principal: " + p.getName()));如果输出中没有
user_role,说明Keycloak未将角色同步到JAAS,需要检查Keycloak客户端的角色配置(确保客户端已关联该角色,且用户拥有该角色)。清理旧LDAP残留配置
删除standalone.xml中未使用的LDAP安全域,检查应用的ejb-jar.xml或jboss-ejb3.xml,确保没有指定旧的LDAP安全域或自定义安全配置。
为什么移除@Stateless就正常?
移除@Stateless后,类变为普通POJO,不会触发JBoss的EJB容器安全校验,直接使用Web层的HttpServletRequest角色上下文,因此@RolesAllowed注解实际未被容器校验,自然不会抛出异常。
内容的提问来源于stack exchange,提问作者h.karimi

