SpringCloud Feign HTTPS请求外部接口

SpringCloud Feign HTTPS请求

背景

项目需要调用外部接口,外部资金方的接口安全性较高,有如下要求:

  • 使用HTTPS调用,
  • 对请求报文进行算法签名,签名后结果需要添加到请求parameter中
  1. 生成一个 32 位的随机字符串 nonce,将 各个请求参数连同 ticket、nonce 两个参数进行字典序排序,将排序后的所有参数字符串拼接成一个字符串进行 SHA1 编码
  2. SHA1 编码后的 40 位字符串作为 sign,sign需要作为参数添加到url中例如url?sign=xxxx

现状

实现方式有多种,可以编写统一的HTTP CLIENT POST方法,带上HTTPS证书,并且请求之前加签, 需要自定义重试。
偶然间发现在编写HTTP CLIENT代码跟Spring Cloud Feign十分相像,代码还没Spring写的精妙,考虑使用Feign直接调用外部接口

代码

  1. 通过指定URL即可直接访问第三方HTTP接口
@FeignClient(value = "WEBANK", url = "https://xxx:443/api/s/", configuration = WebbankConfiguration.class)
public interface FeignSsl {
    @PostMapping(value = "/16080")
    QueryOrderStatusRespDto queryOrder(@RequestBody WebankOrderQueryDto webankOrderQueryDto);
}

    2. 增加SSL证书,需要准备keystore.jks、truststore.jks ,本例将证书直接BASE64 转义,可不用使用该方式,直接使用文件。 WebbankConfiguration代码如下:

import feign.Client;
import feign.Logger;
import feign.codec.Encoder;
import feign.jackson.JacksonEncoder;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.ssl.SSLContexts;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import sun.misc.BASE64Decoder;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.*;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

/**
 * Demo class
 *
 * @date 2019/5/28 下午2:28
 */
@Configuration
@Slf4j
public class WebbankConfiguration {
    private static final String keyStore = "keyStore";//该字段为keystore.jks BASE64 值
    private static final String trustStore = "trustStore";//该字段为keystore.jks BASE64 值
    private static final String password = "12345678";

    @Bean
    public Client feignClient() {
        Client trustSSLSockets = new Client.Default(getSSLSocketFactory(), new NoopHostnameVerifier());
        log.info("feignClient called");
        return trustSSLSockets;
    }


    //增加SSL
    public static SSLSocketFactory getSSLSocketFactory() {
    //TODO Exception 需要抛出异常
        SSLContext sslContext = null;
        BASE64Decoder decoder = new BASE64Decoder();
        InputStream keyStoreInput = null;
        InputStream trustStoreInput = null;
        String keyStoreBaseStr = keyStore;
        String userIdBaseStr = trustStore;
        try {
            byte[] keyStoreBytes = decoder.decodeBuffer(keyStoreBaseStr);
            byte[] trustStoreBytes = decoder.decodeBuffer(userIdBaseStr);
            KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
            keyStoreInput = new ByteArrayInputStream(keyStoreBytes);
            keyStore.load(keyStoreInput, password.toCharArray());

            KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
            trustStoreInput = new ByteArrayInputStream(trustStoreBytes);
            trustStore.load(trustStoreInput, null);
            sslContext = SSLContexts.custom().loadKeyMaterial(keyStore, password.toCharArray())
                    .loadTrustMaterial(trustStore, new TrustSelfSignedStrategy()).build();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (KeyManagementException e) {
            e.printStackTrace();
        } catch (KeyStoreException e) {
            e.printStackTrace();
        } catch (CertificateException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (UnrecoverableKeyException e) {
            e.printStackTrace();
        }
        return sslContext.getSocketFactory();
    }

    //方便查看日志,可不用编写
    @Bean
    Logger.Level feignLoggerLevel() {
        return Logger.Level.FULL;

    }

    /**
     * 增加请求参数加签
     * @param webankOpenApiConfig
     * @return
     */
    @Bean
    WebBankRequestFeignInterceptor webBankRequestFeignInterceptor(@Autowired WebankOpenApiConfig webankOpenApiConfig) {
        return new WebBankRequestFeignInterceptor(webankOpenApiConfig);
    }
}

  3. 增加参数验签,feign提供了RequestInterceptor,只要实现了该接口就可以加请求发送之前增加请求头或者对报文增加参数

import feign.RequestInterceptor;
import feign.RequestTemplate;

/**
 * Demo class
 *
 * @date 2019/5/29 上午11:11
 */
public class WebBankRequestFeignInterceptor implements RequestInterceptor {

    private WebankOpenApiConfig webankOpenApiConfig;

    public WebBankRequestFeignInterceptor() {
    }

    public WebBankRequestFeignInterceptor(WebankOpenApiConfig webankOpenApiConfig) {
        this.webankOpenApiConfig = webankOpenApiConfig;
    }

    public WebankOpenApiConfig getWebankOpenApiConfig() {
        return webankOpenApiConfig;
    }

    public void setWebankOpenApiConfig(WebankOpenApiConfig webankOpenApiConfig) {
        this.webankOpenApiConfig = webankOpenApiConfig;
    }

    @Override
    public void apply(RequestTemplate template) {
        System.out.println("##########template:"+template);
        String parameters = "";
        try {
            //根据报文BODY计算出验签规则
            //返回结果为app_id=XXX&nonce=XX&sign=XXXXX&version=1.0.0
            parameters = webankOpenApiConfig.parametersFormat(null, new String(template.body()), "1000000");
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (ChannelException e) {
            e.printStackTrace();
        }
        System.out.println("##########parameters:" + parameters);
        //TODO 考虑有请求参数相关内容,解决?号,在请求URL后增加URL参数
        template.insert(template.url().length(),"?"+parameters);
    }


}

请求参数,省略其他不需要参数

@Data
public class WebankOrderQueryDto implements Serializable{
    private static final long serialVersionUID = 1L;
    @JsonProperty("MERCHANT_ID")
    private String merchantId;
    @JsonProperty("REQ_TIME")
    private String reqTime;
    @JsonProperty("TXN_ID")
    private String txnId;
}
  • 4
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值