聊聊AsyncHttpClient的SignatureCalculator

2023-12-19 17:19:56 浏览数 (2)

本文主要研究一下AsyncHttpClient的SignatureCalculator

SignatureCalculator

org/asynchttpclient/SignatureCalculator.java

代码语言:javascript复制
/**
 * Interface that allows injecting signature calculator into
 * {@link RequestBuilder} so that signature calculation and inclusion can
 * be added as a pluggable component.
 *
 * @since 1.1
 */
public interface SignatureCalculator {
  /**
   * Method called when {@link RequestBuilder#build} method is called.
   * Should first calculate signature information and then modify request
   * (using passed {@link RequestBuilder}) to add signature (usually as
   * an HTTP header).
   *
   * @param requestBuilder builder that can be used to modify request, usually
   *                       by adding header that includes calculated signature. Be sure NOT to
   *                       call {@link RequestBuilder#build} since this will cause infinite recursion
   * @param request        Request that is being built; needed to access content to
   *                       be signed
   */
  void calculateAndAddSignature(Request request,
                                RequestBuilderBase<?> requestBuilder);
}

SignatureCalculator定义了calculateAndAddSignature接口,它首先计算签名,然后添加到requestBuilder

OAuthSignatureCalculator

org/asynchttpclient/oauth/OAuthSignatureCalculator.java

代码语言:javascript复制
/**
 * OAuth {@link SignatureCalculator} that delegates to {@link OAuthSignatureCalculatorInstance}s.
 */
public class OAuthSignatureCalculator implements SignatureCalculator {

  private static final ThreadLocal<OAuthSignatureCalculatorInstance> INSTANCES = ThreadLocal.withInitial(() -> {
    try {
      return new OAuthSignatureCalculatorInstance();
    } catch (NoSuchAlgorithmException e) {
      throw new ExceptionInInitializerError(e);
    }
  });

  private final ConsumerKey consumerAuth;

  private final RequestToken userAuth;

  /**
   * @param consumerAuth Consumer key to use for signature calculation
   * @param userAuth     Request/access token to use for signature calculation
   */
  public OAuthSignatureCalculator(ConsumerKey consumerAuth, RequestToken userAuth) {
    this.consumerAuth = consumerAuth;
    this.userAuth = userAuth;
  }

  @Override
  public void calculateAndAddSignature(Request request, RequestBuilderBase<?> requestBuilder) {
    try {
      INSTANCES.get().sign(consumerAuth, userAuth, request, requestBuilder);
    } catch (InvalidKeyException e) {
      throw new IllegalArgumentException("Failed to compute a valid key from consumer and user secrets", e);
    }
  }
}

OAuthSignatureCalculator实现了SignatureCalculator接口,其calculateAndAddSignature方法使用了OAuthSignatureCalculatorInstance的sign方法

sign

org/asynchttpclient/oauth/OAuthSignatureCalculatorInstance.java

代码语言:javascript复制
  public void sign(ConsumerKey consumerAuth, RequestToken userAuth, Request request, RequestBuilderBase<?> requestBuilder) throws InvalidKeyException {
    String nonce = generateNonce();
    long timestamp = generateTimestamp();
    sign(consumerAuth, userAuth, request, requestBuilder, timestamp, nonce);
  }

  void sign(ConsumerKey consumerAuth, RequestToken userAuth, Request request, RequestBuilderBase<?> requestBuilder, long timestamp, String nonce) throws InvalidKeyException {
    String percentEncodedNonce = Utf8UrlEncoder.percentEncodeQueryElement(nonce);
    String signature = calculateSignature(consumerAuth, userAuth, request, timestamp, percentEncodedNonce);
    String headerValue = constructAuthHeader(consumerAuth, userAuth, signature, timestamp, percentEncodedNonce);
    requestBuilder.setHeader(HttpHeaderNames.AUTHORIZATION, headerValue);
  }  

  String calculateSignature(ConsumerKey consumerAuth, RequestToken userAuth, Request request, long oauthTimestamp, String percentEncodedNonce) throws InvalidKeyException {

    StringBuilder sb = signatureBaseString(consumerAuth, userAuth, request, oauthTimestamp, percentEncodedNonce);

    ByteBuffer rawBase = StringUtils.charSequence2ByteBuffer(sb, UTF_8);
    byte[] rawSignature = digest(consumerAuth, userAuth, rawBase);
    // and finally, base64 encoded... phew!
    return Base64.encode(rawSignature);
  }  

OAuthSignatureCalculatorInstance的sign方法先生成nonce及timestamp,然后通过calculateSignature计算signature,再通过constructAuthHeader计算headerValue,最后设置到requestBuilder的名为authorization的header

executeSignatureCalculator

org/asynchttpclient/RequestBuilderBase.java

代码语言:javascript复制
  private RequestBuilderBase<?> executeSignatureCalculator() {
    if (signatureCalculator == null)
      return this;

    // build a first version of the request, without signatureCalculator in play
    RequestBuilder rb = new RequestBuilder(this.method);
    // make copy of mutable collections so we don't risk affecting
    // original RequestBuilder
    // call setFormParams first as it resets other fields
    if (this.formParams != null)
      rb.setFormParams(this.formParams);
    if (this.headers != null)
      rb.headers.add(this.headers);
    if (this.cookies != null)
      rb.setCookies(this.cookies);
    if (this.bodyParts != null)
      rb.setBodyParts(this.bodyParts);

    // copy all other fields
    // but rb.signatureCalculator, that's the whole point here
    rb.uriEncoder = this.uriEncoder;
    rb.queryParams = this.queryParams;
    rb.uri = this.uri;
    rb.address = this.address;
    rb.localAddress = this.localAddress;
    rb.byteData = this.byteData;
    rb.compositeByteData = this.compositeByteData;
    rb.stringData = this.stringData;
    rb.byteBufferData = this.byteBufferData;
    rb.streamData = this.streamData;
    rb.bodyGenerator = this.bodyGenerator;
    rb.virtualHost = this.virtualHost;
    rb.proxyServer = this.proxyServer;
    rb.realm = this.realm;
    rb.file = this.file;
    rb.followRedirect = this.followRedirect;
    rb.requestTimeout = this.requestTimeout;
    rb.rangeOffset = this.rangeOffset;
    rb.charset = this.charset;
    rb.channelPoolPartitioning = this.channelPoolPartitioning;
    rb.nameResolver = this.nameResolver;
    Request unsignedRequest = rb.build();
    signatureCalculator.calculateAndAddSignature(unsignedRequest, rb);
    return rb;
  }

executeSignatureCalculator会执行signatureCalculator.calculateAndAddSignature(unsignedRequest, rb)

build

org/asynchttpclient/RequestBuilderBase.java

代码语言:javascript复制
  public Request build() {
    updateCharset();
    RequestBuilderBase<?> rb = executeSignatureCalculator();
    Uri finalUri = rb.computeUri();

    // make copies of mutable internal collections
    List<Cookie> cookiesCopy = rb.cookies == null ? Collections.emptyList() : new ArrayList<>(rb.cookies);
    List<Param> formParamsCopy = rb.formParams == null ? Collections.emptyList() : new ArrayList<>(rb.formParams);
    List<Part> bodyPartsCopy = rb.bodyParts == null ? Collections.emptyList() : new ArrayList<>(rb.bodyParts);

    return new DefaultRequest(rb.method,
            finalUri,
            rb.address,
            rb.localAddress,
            rb.headers,
            cookiesCopy,
            rb.byteData,
            rb.compositeByteData,
            rb.stringData,
            rb.byteBufferData,
            rb.streamData,
            rb.bodyGenerator,
            formParamsCopy,
            bodyPartsCopy,
            rb.virtualHost,
            rb.proxyServer,
            rb.realm,
            rb.file,
            rb.followRedirect,
            rb.requestTimeout,
            rb.readTimeout,
            rb.rangeOffset,
            rb.charset,
            rb.channelPoolPartitioning,
            rb.nameResolver);
  }

RequestBuilderBase的build方法会执行executeSignatureCalculator

小结

SignatureCalculator定义了calculateAndAddSignature接口,它首先计算签名,然后添加到requestBuilder;它有个实现类为OAuthSignatureCalculator;RequestBuilderBase的build方法会执行executeSignatureCalculator。

0 人点赞