Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitfbe8049

Browse files
committed
Optimize OAuthSignatureCalculator ,closeAsyncHttpClient#1333
Motivation:* `ThreadSafeHMAC` is synchronized. `NONCE_BUFFER` uses a`ThreadLocal`, why not use a global one so Mac doesn't needsynchronization?* `OAuthParameterSet. sortAndConcat` allocates a `StringBuilder`* `OAuthParameterSet. sortAndConcat` allocates a an Array.Modifications:* Introduce OAuthSignatureCalculatorInstance and haveOAuthSignatureCalculator delegate* Use `Collections.sort` to sort parametersResult:Less allocation. No more synchronized block.
1 parent328f471 commitfbe8049

File tree

8 files changed

+452
-426
lines changed

8 files changed

+452
-426
lines changed

‎client/src/main/java/org/asynchttpclient/oauth/OAuthSignatureCalculator.java‎

Lines changed: 17 additions & 270 deletions
Original file line numberDiff line numberDiff line change
@@ -13,300 +13,47 @@
1313
*/
1414
packageorg.asynchttpclient.oauth;
1515

16-
importstaticjava.nio.charset.StandardCharsets.UTF_8;
17-
importstaticorg.asynchttpclient.util.MiscUtils.isNonEmpty;
16+
importjava.security.InvalidKeyException;
17+
importjava.security.NoSuchAlgorithmException;
1818

19-
importjava.nio.ByteBuffer;
20-
importjava.util.ArrayList;
21-
importjava.util.Arrays;
22-
importjava.util.List;
23-
importjava.util.concurrent.ThreadLocalRandom;
24-
importjava.util.regex.Pattern;
25-
26-
importorg.asynchttpclient.Param;
2719
importorg.asynchttpclient.Request;
2820
importorg.asynchttpclient.RequestBuilderBase;
2921
importorg.asynchttpclient.SignatureCalculator;
30-
importorg.asynchttpclient.uri.Uri;
31-
importorg.asynchttpclient.util.Base64;
32-
importorg.asynchttpclient.util.StringUtils;
33-
importorg.asynchttpclient.util.Utf8UrlEncoder;
3422

3523
/**
36-
* Simple OAuth signature calculator that can used for constructing client signatures for accessing services that use OAuth for authorization. <br>
37-
* Supports most common signature inclusion and calculation methods: HMAC-SHA1 for calculation, and Header inclusion as inclusion method. Nonce generation uses simple random
38-
* numbers with base64 encoding.
39-
*
40-
* @author tatu (tatu.saloranta@iki.fi)
24+
* OAuth {@link SignatureCalculator} that delegates to {@link OAuthSignatureCalculatorInstance}s.
4125
*/
4226
publicclassOAuthSignatureCalculatorimplementsSignatureCalculator {
43-
publicfinalstaticStringHEADER_AUTHORIZATION ="Authorization";
44-
45-
privatestaticfinalStringKEY_OAUTH_CONSUMER_KEY ="oauth_consumer_key";
46-
privatestaticfinalStringKEY_OAUTH_NONCE ="oauth_nonce";
47-
privatestaticfinalStringKEY_OAUTH_SIGNATURE ="oauth_signature";
48-
privatestaticfinalStringKEY_OAUTH_SIGNATURE_METHOD ="oauth_signature_method";
49-
privatestaticfinalStringKEY_OAUTH_TIMESTAMP ="oauth_timestamp";
50-
privatestaticfinalStringKEY_OAUTH_TOKEN ="oauth_token";
51-
privatestaticfinalStringKEY_OAUTH_VERSION ="oauth_version";
5227

53-
privatestaticfinalStringOAUTH_VERSION_1_0 ="1.0";
54-
privatestaticfinalStringOAUTH_SIGNATURE_METHOD ="HMAC-SHA1";
55-
56-
protectedstaticfinalThreadLocal<byte[]>NONCE_BUFFER =newThreadLocal<byte[]>() {
57-
protectedbyte[]initialValue() {
58-
returnnewbyte[16];
59-
}
28+
privatestaticfinalThreadLocal<OAuthSignatureCalculatorInstance>INSTANCES =newThreadLocal<OAuthSignatureCalculatorInstance>() {
29+
protectedOAuthSignatureCalculatorInstanceinitialValue() {
30+
try {
31+
returnnewOAuthSignatureCalculatorInstance();
32+
}catch (NoSuchAlgorithmExceptione) {
33+
thrownewExceptionInInitializerError(e);
34+
}
35+
};
6036
};
6137

62-
protectedfinalThreadSafeHMACmac;
63-
64-
protectedfinalConsumerKeyconsumerAuth;
38+
privatefinalConsumerKeyconsumerAuth;
6539

66-
protectedfinalRequestTokenuserAuth;
40+
privatefinalRequestTokenuserAuth;
6741

6842
/**
6943
* @param consumerAuth Consumer key to use for signature calculation
7044
* @param userAuth Request/access token to use for signature calculation
7145
*/
7246
publicOAuthSignatureCalculator(ConsumerKeyconsumerAuth,RequestTokenuserAuth) {
73-
mac =newThreadSafeHMAC(consumerAuth,userAuth);
7447
this.consumerAuth =consumerAuth;
7548
this.userAuth =userAuth;
7649
}
7750

7851
@Override
7952
publicvoidcalculateAndAddSignature(Requestrequest,RequestBuilderBase<?>requestBuilder) {
80-
Stringnonce =generateNonce();
81-
longtimestamp =generateTimestamp();
82-
Stringsignature =calculateSignature(request,timestamp,nonce);
83-
StringheaderValue =constructAuthHeader(signature,nonce,timestamp);
84-
requestBuilder.setHeader(HEADER_AUTHORIZATION,headerValue);
85-
}
86-
87-
privateStringencodedParams(longoauthTimestamp,Stringnonce,List<Param>formParams,List<Param>queryParams) {
88-
/**
89-
* List of all query and form parameters added to this request; needed for calculating request signature
90-
*/
91-
intallParametersSize =5 + (userAuth.getKey() !=null ?1 :0) + (formParams !=null ?formParams.size() :0) + (queryParams !=null ?queryParams.size() :0);
92-
OAuthParameterSetallParameters =newOAuthParameterSet(allParametersSize);
93-
94-
// start with standard OAuth parameters we need
95-
allParameters.add(KEY_OAUTH_CONSUMER_KEY,Utf8UrlEncoder.percentEncodeQueryElement(consumerAuth.getKey()));
96-
allParameters.add(KEY_OAUTH_NONCE,Utf8UrlEncoder.percentEncodeQueryElement(nonce));
97-
allParameters.add(KEY_OAUTH_SIGNATURE_METHOD,OAUTH_SIGNATURE_METHOD);
98-
allParameters.add(KEY_OAUTH_TIMESTAMP,String.valueOf(oauthTimestamp));
99-
if (userAuth.getKey() !=null) {
100-
allParameters.add(KEY_OAUTH_TOKEN,Utf8UrlEncoder.percentEncodeQueryElement(userAuth.getKey()));
101-
}
102-
allParameters.add(KEY_OAUTH_VERSION,OAUTH_VERSION_1_0);
103-
104-
if (formParams !=null) {
105-
for (Paramparam :formParams) {
106-
// formParams are not already encoded
107-
allParameters.add(Utf8UrlEncoder.percentEncodeQueryElement(param.getName()),Utf8UrlEncoder.percentEncodeQueryElement(param.getValue()));
108-
}
109-
}
110-
if (queryParams !=null) {
111-
for (Paramparam :queryParams) {
112-
// queryParams are already form-url-encoded
113-
// but OAuth1 uses RFC3986_UNRESERVED_CHARS so * and + have to be encoded
114-
allParameters.add(percentEncodeAlreadyFormUrlEncoded(param.getName()),percentEncodeAlreadyFormUrlEncoded(param.getValue()));
115-
}
116-
}
117-
returnallParameters.sortAndConcat();
118-
}
119-
120-
privateStringbaseUrl(Uriuri) {
121-
/*
122-
* 07-Oct-2010, tatu: URL may contain default port number; if so, need to remove from base URL.
123-
*/
124-
Stringscheme =uri.getScheme();
125-
126-
StringBuildersb =StringUtils.stringBuilder();
127-
sb.append(scheme).append("://").append(uri.getHost());
128-
129-
intport =uri.getPort();
130-
if (scheme.equals("http")) {
131-
if (port ==80)
132-
port = -1;
133-
}elseif (scheme.equals("https")) {
134-
if (port ==443)
135-
port = -1;
136-
}
137-
138-
if (port != -1)
139-
sb.append(':').append(port);
140-
141-
if (isNonEmpty(uri.getPath()))
142-
sb.append(uri.getPath());
143-
144-
returnsb.toString();
145-
}
146-
147-
privatestaticfinalPatternSTAR_CHAR_PATTERN =Pattern.compile("*",Pattern.LITERAL);
148-
privatestaticfinalPatternPLUS_CHAR_PATTERN =Pattern.compile("+",Pattern.LITERAL);
149-
privatestaticfinalPatternENCODED_TILDE_PATTERN =Pattern.compile("%7E",Pattern.LITERAL);
150-
151-
privateStringpercentEncodeAlreadyFormUrlEncoded(Strings) {
152-
s =STAR_CHAR_PATTERN.matcher(s).replaceAll("%2A");
153-
s =PLUS_CHAR_PATTERN.matcher(s).replaceAll("%20");
154-
s =ENCODED_TILDE_PATTERN.matcher(s).replaceAll("~");
155-
returns;
156-
}
157-
158-
StringBuildersignatureBaseString(Requestrequest,longoauthTimestamp,Stringnonce) {
159-
160-
// beware: must generate first as we're using pooled StringBuilder
161-
StringbaseUrl =baseUrl(request.getUri());
162-
StringencodedParams =encodedParams(oauthTimestamp,nonce,request.getFormParams(),request.getQueryParams());
163-
164-
StringBuildersb =StringUtils.stringBuilder();
165-
sb.append(request.getMethod());// POST / GET etc (nothing to URL encode)
166-
sb.append('&');
167-
Utf8UrlEncoder.encodeAndAppendPercentEncoded(sb,baseUrl);
168-
169-
// and all that needs to be URL encoded (... again!)
170-
sb.append('&');
171-
Utf8UrlEncoder.encodeAndAppendPercentEncoded(sb,encodedParams);
172-
returnsb;
173-
}
174-
175-
StringcalculateSignature(Requestrequest,longoauthTimestamp,Stringnonce) {
176-
177-
StringBuildersb =signatureBaseString(request,oauthTimestamp,nonce);
178-
179-
ByteBufferrawBase =StringUtils.charSequence2ByteBuffer(sb,UTF_8);
180-
byte[]rawSignature =mac.digest(rawBase);
181-
// and finally, base64 encoded... phew!
182-
returnBase64.encode(rawSignature);
183-
}
184-
185-
StringconstructAuthHeader(Stringsignature,Stringnonce,longoauthTimestamp) {
186-
StringBuildersb =StringUtils.stringBuilder();
187-
sb.append("OAuth ");
188-
sb.append(KEY_OAUTH_CONSUMER_KEY).append("=\"").append(consumerAuth.getKey()).append("\", ");
189-
if (userAuth.getKey() !=null) {
190-
sb.append(KEY_OAUTH_TOKEN).append("=\"").append(userAuth.getKey()).append("\", ");
191-
}
192-
sb.append(KEY_OAUTH_SIGNATURE_METHOD).append("=\"").append(OAUTH_SIGNATURE_METHOD).append("\", ");
193-
194-
// careful: base64 has chars that need URL encoding:
195-
sb.append(KEY_OAUTH_SIGNATURE).append("=\"");
196-
Utf8UrlEncoder.encodeAndAppendPercentEncoded(sb,signature).append("\", ");
197-
sb.append(KEY_OAUTH_TIMESTAMP).append("=\"").append(oauthTimestamp).append("\", ");
198-
199-
// also: nonce may contain things that need URL encoding (esp. when using base64):
200-
sb.append(KEY_OAUTH_NONCE).append("=\"");
201-
Utf8UrlEncoder.encodeAndAppendPercentEncoded(sb,nonce);
202-
sb.append("\", ");
203-
204-
sb.append(KEY_OAUTH_VERSION).append("=\"").append(OAUTH_VERSION_1_0).append("\"");
205-
returnsb.toString();
206-
}
207-
208-
longgenerateTimestamp() {
209-
returnSystem.currentTimeMillis() /1000L;
210-
}
211-
212-
StringgenerateNonce() {
213-
byte[]nonceBuffer =NONCE_BUFFER.get();
214-
ThreadLocalRandom.current().nextBytes(nonceBuffer);
215-
// let's use base64 encoding over hex, slightly more compact than hex or decimals
216-
returnBase64.encode(nonceBuffer);
217-
// return String.valueOf(Math.abs(random.nextLong()));
218-
}
219-
220-
/**
221-
* Container for parameters used for calculating OAuth signature. About the only confusing aspect is that of whether entries are to be sorted before encoded or vice versa: if
222-
* my reading is correct, encoding is to occur first, then sorting; although this should rarely matter (since sorting is primary by key, which usually has nothing to encode)...
223-
* of course, rarely means that when it would occur it'd be harder to track down.
224-
*/
225-
finalstaticclassOAuthParameterSet {
226-
privatefinalArrayList<Parameter>allParameters;
227-
228-
publicOAuthParameterSet(intsize) {
229-
allParameters =newArrayList<>(size);
230-
}
231-
232-
publicOAuthParameterSetadd(Stringkey,Stringvalue) {
233-
allParameters.add(newParameter(key,value));
234-
returnthis;
235-
}
236-
237-
publicStringsortAndConcat() {
238-
// then sort them (AFTER encoding, important)
239-
Parameter[]params =allParameters.toArray(newParameter[allParameters.size()]);
240-
Arrays.sort(params);
241-
242-
// and build parameter section using pre-encoded pieces:
243-
StringBuilderencodedParams =newStringBuilder(100);
244-
for (Parameterparam :params) {
245-
if (encodedParams.length() >0) {
246-
encodedParams.append('&');
247-
}
248-
encodedParams.append(param.key()).append('=').append(param.value());
249-
}
250-
returnencodedParams.toString();
251-
}
252-
}
253-
254-
/**
255-
* Helper class for sorting query and form parameters that we need
256-
*/
257-
finalstaticclassParameterimplementsComparable<Parameter> {
258-
259-
privatefinalStringkey,value;
260-
261-
publicParameter(Stringkey,Stringvalue) {
262-
this.key =key;
263-
this.value =value;
264-
}
265-
266-
publicStringkey() {
267-
returnkey;
268-
}
269-
270-
publicStringvalue() {
271-
returnvalue;
272-
}
273-
274-
@Override
275-
publicintcompareTo(Parameterother) {
276-
intdiff =key.compareTo(other.key);
277-
if (diff ==0) {
278-
diff =value.compareTo(other.value);
279-
}
280-
returndiff;
281-
}
282-
283-
@Override
284-
publicStringtoString() {
285-
returnkey +"=" +value;
286-
}
287-
288-
@Override
289-
publicbooleanequals(Objecto) {
290-
if (this ==o)
291-
returntrue;
292-
if (o ==null ||getClass() !=o.getClass())
293-
returnfalse;
294-
295-
Parameterparameter = (Parameter)o;
296-
297-
if (!key.equals(parameter.key))
298-
returnfalse;
299-
if (!value.equals(parameter.value))
300-
returnfalse;
301-
302-
returntrue;
303-
}
304-
305-
@Override
306-
publicinthashCode() {
307-
intresult =key.hashCode();
308-
result =31 *result +value.hashCode();
309-
returnresult;
53+
try {
54+
INSTANCES.get().sign(consumerAuth,userAuth,request,requestBuilder);
55+
}catch (InvalidKeyExceptione) {
56+
thrownewIllegalArgumentException("Failed to compute a valid key from consumer and user secrets",e);
31057
}
31158
}
31259
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp