使用 Feign 客户端和证书调用 WS
Call WS with Feign Client and Certificate
我正在努力解决基本的接缝问题,但无法理解它。我有一个 spring 引导应用程序,它应该公开一个休息 url。
我是这样做的:
@RestController
@RequestMapping(value = "/api")
public class MdmhController {
@Resource
private MdmhClient mdmhClient;
@RequestMapping(
method = RequestMethod.GET,
value = "/myEntityNames",
produces = { MediaType.APPLICATION_JSON_UTF8_VALUE }
)
ResponseEntity<Iterable<String>> getMyEntityNames() {
MyEntity[] myEntities =
mdmhClient.getMyentitis();
Set<String> myEntityNames= new HashSet<>();
for (MyEntity me : myEntities ) {
myEntityNames.add(me.getName());
}
return new ResponseEntity<Iterable<String>>(myEntityNames, HttpStatus.OK);
}
}
如您所见,它使用了我试图用假客户端实现的另一项服务:
@Import(FeignClientsConfiguration.class)
@Component
public class MdmhClientImpl implements MdmhClient {
private final Decoder decoder;
private final Encoder encoder;
private MdmhClient mdmhClient;
@Value("${mdmh.serviceId}") // injected by sprins yaml e.g. url-to-service.com
private String mdmhServiceId;
@Autowired
public MdmhClientImpl(
final Decoder decoder, final Encoder encoder) {
this.decoder = decoder;
this.encoder = encoder;
}
@Override
public MyEntity[] getMyEntities() {
if (mdmhClient == null) {
mdmhClient = Feign.builder()
.encoder(encoder)
.decoder(decoder)
.client(new Client.Default(TrustingSSLSocketFactory.get(), null))
.target(MdmhClient.class, "https://" + mdmhServiceId);
}
return mdmhClient.getMyEntity();
}
}
界面看起来像:
@RestController
@RequestMapping(value = "/api")
public interface MdmhClient {
@RequestLine("GET mdmh/service/v2/myentities")
@Headers({ "accept: application/json" })
MyEntity[] getMyEntities();
}
当 MdmhClient 在 mdmhClient.getEntity()
调用时给我异常:
SunCertPathBuilderException: unable to find valid certification path to requested target.
我知道要解决这个问题,我需要将证书导入 jre。我是 运行 Intellij IDE 将我的项目 jdk 的路径设置为:
C:\Program Files\Java\jdk1.8.0_65
我也是通过firefox访问webservice的:
https://url-to-service.com/mdmh/service/v2/myentities
并下载了我导入的证书:
C:\Program Files\Java\jdk1.8.0_65\jre\lib\security\cacerts
但我仍然遇到错误。出于沮丧,我将证书导入所有已安装的 jdks,仍然相同。
并将其添加到我的 MdmhClient 中,例如:
@Override
public MyEntity[] getMyEntities() {
if (mdmhClient == null) {
Client client = new Client.Default(
TrustingSSLSocketFactory.get(),
new HostnameVerifier() {
@Override
public boolean verify(String s, SSLSession sslSession) {
return true;
}
});
mdmhClient = Feign.builder()
.encoder(encoder)
.decoder(decoder)
.client(new Client.Default(TrustingSSLSocketFactory.get(), null))
.target(MdmhClient.class, "https://" + mdmhServiceId);
}
return mdmhClient.getMyEntities();
}
在此之后,我从调用的服务中收到 AccessDenied 响应。
ERROR [081-exec-3] 17.08.17 08:26:28.868 org.apache.juli.logging.DirectJDKLog@log: Servlet.service() for servlet [dispatcherServlet] in context with path [/lic] threw exception [Request processing failed; nested exception is feign.FeignException: status 403 reading MdmhClient#getFamilyVersions(); content:
<HTML><HEAD>
<TITLE>Access Denied</TITLE>
</HEAD>
<BODY>
<FONT f...
但我 100% 确定我不需要身份验证。因为我可以在不修改 headers 的情况下向浏览器输入 URL 并得到结果。
我希望你能帮助我或给我一些解决方法的提示。
谢谢
您的客户端似乎正在通过代理服务器访问服务。代理服务器需要身份验证,因此以 403 响应并使用不同的证书(链),因此导入您从 Web 服务获得的证书无济于事。
您可以尝试以下覆盖方法
@Bean
public Client feignClient()
{
Client trustSSLSockets = new Client.Default(getSSLSocketFactory(), new NoopHostnameVerifier());
return trustSSLSockets;
}
private SSLSocketFactory getSSLSocketFactory() {
try {
TrustStrategy acceptingTrustStrategy = new TrustStrategy() {
@Override
public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
return true;
}
};
SSLContext sslContext = SSLContexts.custom().loadTrustMaterial(null, acceptingTrustStrategy).build();
return sslContext.getSocketFactory();
} catch (Exception exception) {
}
return null;
}
我正在努力解决基本的接缝问题,但无法理解它。我有一个 spring 引导应用程序,它应该公开一个休息 url。
我是这样做的:
@RestController
@RequestMapping(value = "/api")
public class MdmhController {
@Resource
private MdmhClient mdmhClient;
@RequestMapping(
method = RequestMethod.GET,
value = "/myEntityNames",
produces = { MediaType.APPLICATION_JSON_UTF8_VALUE }
)
ResponseEntity<Iterable<String>> getMyEntityNames() {
MyEntity[] myEntities =
mdmhClient.getMyentitis();
Set<String> myEntityNames= new HashSet<>();
for (MyEntity me : myEntities ) {
myEntityNames.add(me.getName());
}
return new ResponseEntity<Iterable<String>>(myEntityNames, HttpStatus.OK);
}
}
如您所见,它使用了我试图用假客户端实现的另一项服务:
@Import(FeignClientsConfiguration.class)
@Component
public class MdmhClientImpl implements MdmhClient {
private final Decoder decoder;
private final Encoder encoder;
private MdmhClient mdmhClient;
@Value("${mdmh.serviceId}") // injected by sprins yaml e.g. url-to-service.com
private String mdmhServiceId;
@Autowired
public MdmhClientImpl(
final Decoder decoder, final Encoder encoder) {
this.decoder = decoder;
this.encoder = encoder;
}
@Override
public MyEntity[] getMyEntities() {
if (mdmhClient == null) {
mdmhClient = Feign.builder()
.encoder(encoder)
.decoder(decoder)
.client(new Client.Default(TrustingSSLSocketFactory.get(), null))
.target(MdmhClient.class, "https://" + mdmhServiceId);
}
return mdmhClient.getMyEntity();
}
}
界面看起来像:
@RestController
@RequestMapping(value = "/api")
public interface MdmhClient {
@RequestLine("GET mdmh/service/v2/myentities")
@Headers({ "accept: application/json" })
MyEntity[] getMyEntities();
}
当 MdmhClient 在 mdmhClient.getEntity()
调用时给我异常:
SunCertPathBuilderException: unable to find valid certification path to requested target.
我知道要解决这个问题,我需要将证书导入 jre。我是 运行 Intellij IDE 将我的项目 jdk 的路径设置为:
C:\Program Files\Java\jdk1.8.0_65
我也是通过firefox访问webservice的:
https://url-to-service.com/mdmh/service/v2/myentities
并下载了我导入的证书:
C:\Program Files\Java\jdk1.8.0_65\jre\lib\security\cacerts
但我仍然遇到错误。出于沮丧,我将证书导入所有已安装的 jdks,仍然相同。
并将其添加到我的 MdmhClient 中,例如:
@Override
public MyEntity[] getMyEntities() {
if (mdmhClient == null) {
Client client = new Client.Default(
TrustingSSLSocketFactory.get(),
new HostnameVerifier() {
@Override
public boolean verify(String s, SSLSession sslSession) {
return true;
}
});
mdmhClient = Feign.builder()
.encoder(encoder)
.decoder(decoder)
.client(new Client.Default(TrustingSSLSocketFactory.get(), null))
.target(MdmhClient.class, "https://" + mdmhServiceId);
}
return mdmhClient.getMyEntities();
}
在此之后,我从调用的服务中收到 AccessDenied 响应。
ERROR [081-exec-3] 17.08.17 08:26:28.868 org.apache.juli.logging.DirectJDKLog@log: Servlet.service() for servlet [dispatcherServlet] in context with path [/lic] threw exception [Request processing failed; nested exception is feign.FeignException: status 403 reading MdmhClient#getFamilyVersions(); content:
<HTML><HEAD>
<TITLE>Access Denied</TITLE>
</HEAD>
<BODY>
<FONT f...
但我 100% 确定我不需要身份验证。因为我可以在不修改 headers 的情况下向浏览器输入 URL 并得到结果。
我希望你能帮助我或给我一些解决方法的提示。
谢谢
您的客户端似乎正在通过代理服务器访问服务。代理服务器需要身份验证,因此以 403 响应并使用不同的证书(链),因此导入您从 Web 服务获得的证书无济于事。
您可以尝试以下覆盖方法
@Bean
public Client feignClient()
{
Client trustSSLSockets = new Client.Default(getSSLSocketFactory(), new NoopHostnameVerifier());
return trustSSLSockets;
}
private SSLSocketFactory getSSLSocketFactory() {
try {
TrustStrategy acceptingTrustStrategy = new TrustStrategy() {
@Override
public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
return true;
}
};
SSLContext sslContext = SSLContexts.custom().loadTrustMaterial(null, acceptingTrustStrategy).build();
return sslContext.getSocketFactory();
} catch (Exception exception) {
}
return null;
}