代码之家  ›  专栏  ›  技术社区  ›  kism3t

使用外部客户端和证书调用WS

  •  2
  • kism3t  · 技术社区  · 7 年前

    我是这样做的:

    @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.
    

    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
    

    但我还是会出错。出于沮丧,我将证书导入到所有已安装的JDK,仍然是一样的。

    我发现: https://github.com/OpenFeign/feign/blob/master/core/src/test/java/feign/client/TrustingSSLSocketFactory.java

    并将其添加到我的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...
    

    但我百分之百确信我不需要身份验证。因为我可以在不修改标题的情况下输入浏览器的URL并得到结果。

    我希望你能帮助我或给我一些提示如何解决这个问题。

    非常感谢。

    2 回复  |  直到 7 年前
        1
  •  2
  •   Sonata    7 年前

    看起来您的客户端正在通过代理服务器访问服务。代理服务器需要身份验证,因此使用403进行响应,并使用不同的证书(链),因此导入从web服务获得的证书没有帮助。

        2
  •  0
  •   eranda.del    5 年前

    您可以尝试以下替代方法

    @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;
    }