HttpClient-正向代理和验签

7 篇文章 0 订阅

简介

HttpClientBuilder是阿帕奇的一个http客户端构建类,通过继承构建类可以添加验签,实现统一发送请求的时候携带验签。

自定义的MyHttpClientBuilder往责任链中添加新的一环

实践

HttpClientBuilder

HttpClientBuilder有很多public方法,但是都是final定义,子类无法重写。

关键改造在于decorateProtocolExec

@NoArgsConstructor
public class MyHttpClientBuilder extends HttpClientBuilder {

    private String name;
    private String password;

    public static MyHttpClientBuilder create() {
        return new MyHttpClientBuilder();
    }

    public MyHttpClientBuilder setName(String name) {
        this.name = name;
        return this;
    }

    public MyHttpClientBuilder setPassword(String password) {
        this.password = password;
        return this;
    }

    /**
     * 创建主链
     */
    @Override
    protected ClientExecChain createMainExec(HttpRequestExecutor requestExec, HttpClientConnectionManager connManager, ConnectionReuseStrategy reuseStrategy, ConnectionKeepAliveStrategy keepAliveStrategy, HttpProcessor proxyHttpProcessor, AuthenticationStrategy targetAuthStrategy, AuthenticationStrategy proxyAuthStrategy, UserTokenHandler userTokenHandler) {
        return super.createMainExec(requestExec, connManager, reuseStrategy, keepAliveStrategy, proxyHttpProcessor, targetAuthStrategy, proxyAuthStrategy, userTokenHandler);
    }

    /**
     * 装饰主链
     */
    @Override
    protected ClientExecChain decorateMainExec(ClientExecChain mainExec) {
        return super.decorateMainExec(mainExec);
    }

    /**
     * 装饰的
     */
    @Override
    protected ClientExecChain decorateProtocolExec(ClientExecChain protocolExec) {
//        return super.decorateProtocolExec(protocolExec);
        return new MyClientExecChain(name, password, protocolExec);
    }

    /**
     * 针对多客户端管理添加http客户端
     */
    @Override
    protected void addCloseable(Closeable closeable) {
        super.addCloseable(closeable);
    }

    /**
     * 初始化InternalHttpClient
     */
    @Override
    public CloseableHttpClient build() {
        return super.build();
    }
}

自定义责任链

定义责任链添加请求头token

public class MyClientExecChain implements ClientExecChain {

    private final String name;
    private final String password;
    private final ClientExecChain mainExec;

    public MyClientExecChain(String name,String password,ClientExecChain mainExec){
        this.name = name;
        this.password = password;
        this.mainExec = mainExec;
    }

    /**
     * 责任链需要规定自己生效的条件
     * */
    @Override
    public CloseableHttpResponse execute(HttpRoute route,
                                         HttpRequestWrapper request,
                                         HttpClientContext clientContext,
                                         HttpExecutionAware execAware) throws IOException, HttpException {

        // 控制访问的域名
        if ( Objects.nonNull(request.getURI().getHost()) &&  request.getURI().getHost().endsWith("127.0.0.1")) {
            return executeMy(route, request, clientContext, execAware);
        } else {
            return mainExec.execute(route, request, clientContext, execAware);
        }
    }

    /**
     * 添加内容
     * */
    private CloseableHttpResponse executeMy(HttpRoute route, HttpRequestWrapper request, HttpClientContext clientContext,
                                            HttpExecutionAware execAware) throws IOException, HttpException{
        // 添加认证信息
        request.addHeader("token", name +"-"+ password);

        // 执行mainExec
        CloseableHttpResponse response = mainExec.execute(route, request, clientContext, execAware);

        // 对成功应答验签
        StatusLine statusLine = response.getStatusLine();
        if (statusLine.getStatusCode() >= 200 && statusLine.getStatusCode() < 300) {
            // 转换为可重复响应的entity
            convertToRepeatableResponseEntity(response);
            if (!(request.getOriginal() instanceof WxPayV3DownloadHttpGet)) {
                if (!validate(response)) {
                    throw new HttpException("签名验证失败");
                }
            }
        }
        return response;
    }

    /**
     * 转换为可重复响应的entity
     * */
    protected void convertToRepeatableResponseEntity(CloseableHttpResponse response) throws IOException {
        HttpEntity entity = response.getEntity();
        if (entity != null && !entity.isRepeatable()) {
            response.setEntity(newRepeatableEntity(entity));
        }
    }

    /**
     * 新entity
     * */
    protected HttpEntity newRepeatableEntity(HttpEntity entity) throws IOException {
        byte[] content = EntityUtils.toByteArray(entity);
        ByteArrayEntity newEntity = new ByteArrayEntity(content);
        newEntity.setContentEncoding(entity.getContentEncoding());
        newEntity.setContentType(entity.getContentType());

        return newEntity;
    }

    /**
     * 校验
     * */
    private boolean validate(CloseableHttpResponse response) throws IOException {
        // 如果不是json格式,直接返回
        if (!ContentType.APPLICATION_JSON.getMimeType()
                .equals(ContentType.parse(
                        String.valueOf(response.getFirstHeader("Content-Type").getValue())
                ).getMimeType())) {
            return true;
        }
        // 获取头部信息
        Header name = response.getFirstHeader("name");

        // 不存在数据
        if (name == null) {
            return false;
        }

        return !Objects.equals(name,this.name);
    }

}

测试

这里请求的时候127.0.0.1,责任链拦截的域名也是这个

    @Test
    void contextLoads() {

        String url = "http://127.0.0.1:9090/http/test";

        WxPayUnifiedOrderV3Request request = new WxPayUnifiedOrderV3Request();
        // 交易码
        request.setOutTradeNo("5566");
        // 通知地址(回调地址)
        request.setNotifyUrl("https://api.qq.com/");
        request.setDescription("这是一个描述");

        WxPayUnifiedOrderV3Request.Payer payer = new WxPayUnifiedOrderV3Request.Payer();
        // 商户openid
        payer.setOpenid("openid");
        request.setPayer(payer);

        //构建金额信息
        WxPayUnifiedOrderV3Request.Amount amount = new WxPayUnifiedOrderV3Request.Amount();
        //设置币种信息
        amount.setCurrency("CNY");
        //设置金额,这里用分计算
        amount.setTotal(BigDecimal.ONE.multiply(BigDecimal.valueOf(100)).intValue());
        request.setAmount(amount);

        String res = post(url,JSON.toJSONString(request));
        log.info("http请求结束:",res);
    }

    private String post(String url,String entity){

        HttpPost httpPost = new HttpPost(url);
        httpPost.setConfig(RequestConfig.custom()
                // 从连接池获取连接的时间
                .setConnectionRequestTimeout(1000)
                // 建立连接的时间
                .setConnectTimeout(1000)
                // 读取数据的时间
                .setSocketTimeout(3000)
                .build());
        httpPost.setEntity(new StringEntity(entity, ContentType.create("application/json", "utf-8")));

        CloseableHttpClient httpClient = MyHttpClientBuilder
                .create()
                .setName("zhang")
                .setPassword("1234").build();

        try (CloseableHttpResponse response = httpClient.execute(httpPost)) {

            int statusCode = response.getStatusLine().getStatusCode();
            //post方法有可能会没有返回值的情况
            String responseString = null;
            if (response.getEntity() != null) {
                responseString = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);
            }
            //状态码判断200 204 成功
            if (HttpStatus.SC_OK == statusCode || HttpStatus.SC_NO_CONTENT == statusCode) {
                this.log.info("\n【请求地址】:{}\n【请求数据】:{}\n【响应数据】:{}", url, entity, responseString);
                return responseString;
            }
            //有错误提示信息返回
            throw new MyException(responseString);
        } catch (Exception e) {
           log.error("异常:",e);
        } finally {
            // 关闭连接
            httpPost.releaseConnection();
        }
        return null;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值