如何在DOSGi中使用CXF HttpConduitFeature并配置基础认证?
首先咱们拆解下你遇到的核心问题:为什么HttpConduitFeature.Portable的initialize()方法没被触发?
看你贴的JAXRSClientFactoryBean里的applyFeatures方法,它调用的是feature.initialize(client.getConfiguration(), getBus())——这个方法对应Feature接口的void initialize(InterceptorProvider interceptorProvider, Bus bus)签名。但你提供的HttpConduitFeature.Portable只实现了AbstractPortableFeature的void initialize(Client client, Bus bus)方法,两个方法签名不匹配,所以CXF遍历Features时找不到对应的执行入口,自然不会触发你的Conduit配置逻辑。
下面给你几个可行的解决方案,你可以根据场景选择:
方案1:适配HttpConduitFeature,让它支持JAXRS客户端调用
修改HttpConduitFeature.Portable类,让它同时实现Feature接口,新增适配的initialize方法,把配置逻辑抽成通用方法:
public class HttpConduitFeature extends DelegatingFeature<HttpConduitFeature.Portable> { public HttpConduitFeature() { super(new Portable()); } public void setConduitConfig(HttpConduitConfig conduitConfig) { delegate.setConduitConfig(conduitConfig); } public static class Portable implements AbstractPortableFeature, Feature { private HttpConduitConfig conduitConfig; // 保留原有方法,兼容其他场景 @Override public void initialize(Client client, Bus bus) { applyConduitConfig(client, bus); } // 新增适配JAXRSClientFactoryBean的方法 @Override public void initialize(InterceptorProvider interceptorProvider, Bus bus) { if (interceptorProvider instanceof Client) { applyConduitConfig((Client) interceptorProvider, bus); } } // 抽离通用配置逻辑 private void applyConduitConfig(Client client, Bus bus) { Conduit conduit = client.getConduit(); if (conduitConfig != null && conduit instanceof HTTPConduit) { conduitConfig.apply((HTTPConduit)conduit); } } public void setConduitConfig(HttpConduitConfig conduitConfig) { this.conduitConfig = conduitConfig; } } }
修改后,你原来的BasicAuthorizationIntent代码就能正常工作了,CXF现在能找到对应的initialize方法执行配置。
方案2:直接通过Intent配置HTTPConduit(不依赖HttpConduitFeature)
跳过HttpConduitFeature,直接在自定义Intent里获取客户端的HTTPConduit并设置授权策略,逻辑更简洁:
public class BasicAuthorizationIntent implements IntentsProvider { @Override public List<?> getIntents() { return Arrays.asList(new Feature() { @Override public void initialize(InterceptorProvider interceptorProvider, Bus bus) { if (interceptorProvider instanceof Client) { Client client = (Client) interceptorProvider; Conduit conduit = client.getConduit(); if (conduit instanceof HTTPConduit) { HTTPConduit httpConduit = (HTTPConduit) conduit; httpConduit.setAuthorization(basicAuthorization()); } } } }); } private AuthorizationPolicy basicAuthorization() { AuthorizationPolicy authorizationPolicy = new AuthorizationPolicy(); authorizationPolicy.setUserName("dosgi"); authorizationPolicy.setPassword("dosgi"); authorizationPolicy.setAuthorizationType("Basic"); return authorizationPolicy; } }
方案3:使用CXF拦截器添加Authorization头(兼容性最好)
这种方式不依赖HTTPConduit的具体实现,通过添加客户端出站拦截器设置Authorization头,通用性最强:
import org.apache.cxf.message.Message; import org.apache.cxf.phase.AbstractPhaseInterceptor; import org.apache.cxf.phase.Phase; import java.nio.charset.StandardCharsets; import org.apache.commons.codec.binary.Base64; public class BasicAuthorizationIntent implements IntentsProvider { @Override public List<?> getIntents() { return Arrays.asList(new Feature() { @Override public void initialize(InterceptorProvider interceptorProvider, Bus bus) { if (interceptorProvider instanceof Client) { Client client = (Client) interceptorProvider; client.getOutInterceptors().add(new BasicAuthInterceptor()); } } }); } private static class BasicAuthInterceptor extends AbstractPhaseInterceptor<Message> { public BasicAuthInterceptor() { // 选择PRE_PROTOCOL阶段,确保发送请求前添加头 super(Phase.PRE_PROTOCOL); } @Override public void handleMessage(Message message) throws Fault { String credentials = "dosgi:dosgi"; String encodedCredentials = Base64.encodeBase64String(credentials.getBytes(StandardCharsets.UTF_8)); message.put(Message.AUTHORIZATION, "Basic " + encodedCredentials); } } }
方案对比
- 方案1:适配原有
HttpConduitFeature,适合需要复用HttpConduitConfig配置逻辑的场景; - 方案2:直接操作
HTTPConduit,逻辑简洁,适合仅需设置授权的场景; - 方案3:拦截器方式,不依赖具体Conduit实现,兼容性最好,适合跨场景复用。
内容的提问来源于stack exchange,提问作者Steven Huypens

