You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

WildFly 26.1.3中EJB客户端切换服务器二次调用认证失败问题

动态切换WildFly EJB服务器时二次调用出现认证错误问题排查

问题现象

在WildFly 26.1.3环境下,实现EJB客户端动态切换两台WildFly EJB服务器时出现异常,具体流程:

  • 首次调用Server 1成功(创建新Context并完成JNDI查找)
  • 首次调用Server 2成功
  • 二次调用Server 2时触发认证错误
    需求为根据用户请求调用不同服务器的EJB服务。

测试代码

import javax.naming.Context;
import javax.naming.InitialContext;
import java.util.Hashtable;

public class EjbClient {
    private static Context getContext(String serverUrl, String username, String password) {
        Hashtable<String, String> env = new Hashtable<>();
        env.put(Context.INITIAL_CONTEXT_FACTORY, "org.wildfly.naming.client.WildFlyInitialContextFactory");
        env.put(Context.PROVIDER_URL, serverUrl);
        env.put(Context.SECURITY_PRINCIPAL, username);
        env.put(Context.SECURITY_CREDENTIALS, password);
        env.put("jboss.naming.client.ejb.context", "true");
        try {
            return new InitialContext(env);
        } catch (Exception e) {
            throw new RuntimeException("Failed to create context", e);
        }
    }

    public static void callEjb(String serverUrl, String username, String password) {
        try (Context ctx = getContext(serverUrl, username, password)) {
            // 示例EJB接口与JNDI名称,根据实际场景调整
            HelloRemote ejb = (HelloRemote) ctx.lookup("ejb:/ejb-server/HelloBean!" + HelloRemote.class.getName());
            String result = ejb.sayHello();
            System.out.println("调用结果: " + result);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        String server1Url = "http-remoting://server1:8080";
        String server2Url = "http-remoting://server2:8080";

        // 首次调用Server1
        callEjb(server1Url, "user1", "pass1");
        // 首次调用Server2
        callEjb(server2Url, "user2", "pass2");
        // 二次调用Server2,触发认证错误
        callEjb(server2Url, "user2", "pass2");
    }
}

报错输出

javax.naming.AuthenticationException: Failed to connect to any server. Servers tried: [http-remoting://server2:8080 (Authentication failed: all available authentication mechanisms failed:
        DIGEST-MD5: Server rejected authentication)]
        at org.wildfly.naming.client.remote.RemoteNamingStore.failOverOrThrow(RemoteNamingStore.java:243)
        at org.wildfly.naming.client.remote.RemoteNamingStore.connect(RemoteNamingStore.java:163)
        at org.wildfly.naming.client.remote.RemoteNamingStore.lookup(RemoteNamingStore.java:267)
        at org.wildfly.naming.client.remote.RemoteContext.lookup(RemoteContext.java:108)
        at org.wildfly.naming.client.remote.RemoteContext.lookup(RemoteContext.java:126)
        at javax.naming.InitialContext.lookup(InitialContext.java:417)
        at com.example.EjbClient.callEjb(EjbClient.java:22)
        at com.example.EjbClient.main(EjbClient.java:38)
Caused by: javax.security.sasl.SaslException: Authentication failed: all available authentication mechanisms failed:
        DIGEST-MD5: Server rejected authentication
        at org.wildfly.security.sasl.util.AbstractSaslClient.evaluateChallenge(AbstractSaslClient.java:214)
        at org.wildfly.naming.client.remote.RemoteNamingStore.lambda$connect$0(RemoteNamingStore.java:136)
        at org.wildfly.naming.client.remote.RemoteNamingStore.retryConnect(RemoteNamingStore.java:204)
        at org.wildfly.naming.client.remote.RemoteNamingStore.connect(RemoteNamingStore.java:132)
        ... 6 more

问题原因及解决方案

原因分析

WildFly EJB客户端默认会缓存连接与认证上下文,切换服务器后二次调用时,可能复用了前一台服务器的认证凭证,导致目标服务器认证失败;此外InitialContext资源未彻底释放,也会导致连接池中的旧连接被错误复用。

解决办法

  1. 禁用连接复用
    创建InitialContext时添加环境变量,强制每次调用生成新连接,避免复用缓存的认证上下文:

    env.put("jboss.naming.client.connect.options.org.xnio.Options.NO_REUSE", "true");
    
  2. 确保Context彻底释放
    即便使用try-with-resources,部分场景下RemoteContext可能未完全释放连接,可手动在finally块中关闭上下文:

    public static void callEjb(String serverUrl, String username, String password) {
        Context ctx = null;
        try {
            ctx = getContext(serverUrl, username, password);
            HelloRemote ejb = (HelloRemote) ctx.lookup("ejb:/ejb-server/HelloBean!" + HelloRemote.class.getName());
            String result = ejb.sayHello();
            System.out.println("调用结果: " + result);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (ctx != null) {
                try {
                    ctx.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }
    
  3. 使用独立客户端配置
    为每台服务器创建独立的EJBClientConfiguration,避免共享上下文资源:

    import org.wildfly.naming.client.WildFlyInitialContextFactory;
    import org.wildfly.naming.client.config.EJBClientConfiguration;
    import org.wildfly.naming.client.config.PropertiesBasedEJBClientConfiguration;
    
    private static Context getContext(String serverUrl, String username, String password) {
        Hashtable<String, String> props = new Hashtable<>();
        props.put("remote.connectionprovider.create.options.org.xnio.Options.SSL_ENABLED", "false");
        props.put("remote.connections", "default");
        props.put("remote.connection.default.host", serverUrl.split("://")[1].split(":")[0]);
        props.put("remote.connection.default.port", serverUrl.split("://")[1].split(":")[1]);
        props.put("remote.connection.default.username", username);
        props.put("remote.connection.default.password", password);
        props.put("remote.connection.default.connect.options.org.xnio.Options.NO_REUSE", "true");
    
        EJBClientConfiguration config = new PropertiesBasedEJBClientConfiguration(props);
        Hashtable<String, Object> env = new Hashtable<>();
        env.put(WildFlyInitialContextFactory.EJB_CLIENT_CONFIGURATION, config);
        env.put(Context.INITIAL_CONTEXT_FACTORY, WildFlyInitialContextFactory.class.getName());
    
        try {
            return new InitialContext(env);
        } catch (Exception e) {
            throw new RuntimeException("Failed to create context", e);
        }
    }
    
  4. 检查服务器端认证配置
    确认两台服务器的security-domain配置一致,比如standalone.xml中DIGEST-MD5等认证机制的参数,避免因凭证格式或认证规则不匹配导致失败。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 20:25:54