Wildfly 15中Subject.doAs运行EJB不影响EJBContext问题求助
I've run into this exact issue before—when using standard Java SE Subject.doAs or even basic Elytron AuthenticationContext calls, WildFly's EJB container doesn't pick up the security context because it relies on its own internal thread-bound security context, not the generic Java SE one. Here's how to fix it for both traditional security and Elytron setups:
1. First, Fix a Critical Mismatch in Your Code
Looking at your code, your LoginContext uses the security domain "myDomain", but your EJB is annotated with @SecurityDomain("DemoApplicationDomain"). These must match for the security context to be recognized by the EJB container. Update either the LoginContext domain or the EJB's @SecurityDomain value to align.
2. Solution for Traditional WildFly Security Subsystem
Use WildFly's SecurityContextAssociation API to explicitly bind the authenticated subject to the thread's security context—this is what the EJB container looks for:
import org.jboss.security.SecurityContextAssociation; import org.jboss.security.SecurityContextFactory; import org.jboss.security.SecurityContext; import javax.security.auth.login.LoginContext; import javax.security.auth.login.LoginException; public class RunnableHandlerImpl implements RunnableHandler { @Override public void runAsPrivileged(final ContextRunnable runnable) throws LoginException { LoginContext ctx = new LoginContext("DemoApplicationDomain", new MyCallbackHandler(runnable.getAuthToken())); ctx.login(); Subject subject = ctx.getSubject(); // Create and populate WildFly's security context SecurityContext securityContext = SecurityContextFactory.createSecurityContext(); securityContext.setSubject(subject); // Save original context to restore later (critical for thread pool reuse) SecurityContext oldSecurityContext = SecurityContextAssociation.getSecurityContext(); try { // Bind the authenticated context to the thread SecurityContextAssociation.setSecurityContext(securityContext); // Execute your runnable—EJB will now see the correct principal runnable.run(); } finally { // Restore original context and clean up SecurityContextAssociation.setSecurityContext(oldSecurityContext); ctx.logout(); } } }
3. Solution for WildFly Elytron (Recommended)
If you're using Elytron, use the native SecurityIdentity.runAs method—it's designed to properly propagate the security context to EJB calls:
import org.wildfly.security.auth.server.SecurityDomain; import org.wildfly.security.auth.server.SecurityDomainManager; import org.wildfly.security.auth.server.SecurityIdentity; import javax.security.auth.login.LoginException; public class RunnableHandlerImpl implements RunnableHandler { @Override public void runAsPrivileged(final ContextRunnable runnable) throws LoginException { // Get the Elytron security domain matching your EJB's @SecurityDomain SecurityDomain securityDomain = SecurityDomainManager.getInstance().getSecurityDomain("DemoApplicationDomain"); if (securityDomain == null) { throw new LoginException("Elytron security domain not found"); } // Authenticate to get the SecurityIdentity (adjust to use your AuthToken) SecurityIdentity securityIdentity = securityDomain.authenticate( runnable.getAuthToken().getUsername(), runnable.getAuthToken().getPassword().toCharArray() ); // Use SecurityIdentity's runAs to propagate context to EJB securityIdentity.runAs(() -> { try { runnable.run(); } catch (Exception e) { throw new RuntimeException("Failed to run privileged task", e); } return null; }); } }
Key Notes to Avoid Pitfalls
- Thread Context Cleanup: Always restore the original security context in a
finallyblock—WildFly uses thread pools, and leaving the authenticated context bound can cause cross-request leaks. - EJB Reference Serialization: Your
ContextRunnableserializes an EJB reference. While this works in the same JVM, if you ever need to pass this across nodes, replace the direct reference with a JNDI lookup inside therun()method instead. - Security Domain Configuration: Ensure your security domain (either traditional or Elytron) is properly configured in
standalone.xml/domain.xmlwith the correct authentication modules and role mappings.
After applying one of these fixes, your EJBContext.getCallerPrincipal() and SessionContext.getCallerPrincipal() should return the authenticated user instead of "anonymous".
内容的提问来源于stack exchange,提问作者jlange

