1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.springframework.security.oauth.consumer.client;
18
19 import org.apache.commons.codec.DecoderException;
20 import org.apache.commons.logging.Log;
21 import org.apache.commons.logging.LogFactory;
22 import org.springframework.beans.factory.InitializingBean;
23 import org.springframework.beans.factory.annotation.Autowired;
24 import org.springframework.security.oauth.common.OAuthCodec;
25 import org.springframework.security.oauth.common.OAuthConsumerParameter;
26 import org.springframework.security.oauth.common.OAuthProviderParameter;
27 import org.springframework.security.oauth.common.StringSplitUtils;
28 import org.springframework.security.oauth.common.signature.CoreOAuthSignatureMethodFactory;
29 import org.springframework.security.oauth.common.signature.OAuthSignatureMethod;
30 import org.springframework.security.oauth.common.signature.OAuthSignatureMethodFactory;
31 import org.springframework.security.oauth.common.signature.UnsupportedSignatureMethodException;
32 import org.springframework.security.oauth.consumer.InvalidOAuthRealmException;
33 import org.springframework.security.oauth.consumer.OAuthConsumerSupport;
34 import org.springframework.security.oauth.consumer.OAuthConsumerToken;
35 import org.springframework.security.oauth.consumer.OAuthRequestFailedException;
36 import org.springframework.security.oauth.consumer.ProtectedResourceDetails;
37 import org.springframework.security.oauth.consumer.ProtectedResourceDetailsService;
38 import org.springframework.security.oauth.consumer.UnverifiedRequestTokenException;
39 import org.springframework.security.oauth.consumer.net.OAuthURLStreamHandlerFactory;
40 import org.springframework.security.oauth.consumer.nonce.NonceFactory;
41 import org.springframework.security.oauth.consumer.nonce.UUIDNonceFactory;
42 import org.springframework.util.Assert;
43
44 import java.io.*;
45 import java.net.*;
46 import java.util.*;
47
48 import static org.springframework.security.oauth.common.OAuthCodec.oauthEncode;
49
50
51
52
53
54
55
56
57
58 public class CoreOAuthConsumerSupport implements OAuthConsumerSupport, InitializingBean {
59
60 private static final Log logger = LogFactory.getLog(CoreOAuthConsumerSupport.class);
61 private OAuthURLStreamHandlerFactory streamHandlerFactory;
62 private OAuthSignatureMethodFactory signatureFactory = new CoreOAuthSignatureMethodFactory();
63 private NonceFactory nonceFactory = new UUIDNonceFactory();
64
65 private ProtectedResourceDetailsService protectedResourceDetailsService;
66
67 private ProxySelector proxySelector = ProxySelector.getDefault();
68 private int connectionTimeout = 1000 * 60;
69 private int readTimeout = 1000 * 60;
70
71 public CoreOAuthConsumerSupport() {
72 try {
73 this.streamHandlerFactory = ((OAuthURLStreamHandlerFactory) Class.forName("org.springframework.security.oauth.consumer.net.DefaultOAuthURLStreamHandlerFactory").newInstance());
74 }
75 catch (Throwable e) {
76 throw new IllegalStateException(e);
77 }
78 }
79
80 public CoreOAuthConsumerSupport(OAuthURLStreamHandlerFactory streamHandlerFactory) {
81 this.streamHandlerFactory = streamHandlerFactory;
82 }
83
84 public void afterPropertiesSet() throws Exception {
85 Assert.notNull(protectedResourceDetailsService, "A protected resource details service is required.");
86 Assert.notNull(streamHandlerFactory, "A stream handler factory is required.");
87 }
88
89
90 public OAuthConsumerToken getUnauthorizedRequestToken(String resourceId, String callback) throws OAuthRequestFailedException {
91 ProtectedResourceDetails details = getProtectedResourceDetailsService().loadProtectedResourceDetailsById(resourceId);
92 return getUnauthorizedRequestToken(details, callback);
93 }
94
95 public OAuthConsumerToken getUnauthorizedRequestToken(ProtectedResourceDetails details, String callback) throws OAuthRequestFailedException {
96 URL requestTokenURL;
97 try {
98 requestTokenURL = new URL(details.getRequestTokenURL());
99 }
100 catch (MalformedURLException e) {
101 throw new IllegalStateException("Malformed URL for obtaining a request token.", e);
102 }
103
104 String httpMethod = details.getRequestTokenHttpMethod();
105
106 Map<String, String> additionalParameters = new TreeMap<String, String>();
107 if (details.isUse10a()) {
108 additionalParameters.put(OAuthConsumerParameter.oauth_callback.toString(), callback);
109 }
110 Map<String, String> specifiedParams = details.getAdditionalParameters();
111 if (specifiedParams != null) {
112 additionalParameters.putAll(specifiedParams);
113 }
114 return getTokenFromProvider(details, requestTokenURL, httpMethod, null, additionalParameters);
115 }
116
117
118 public OAuthConsumerToken/org/springframework/security/oauth/consumer/OAuthConsumerToken.html#OAuthConsumerToken">OAuthConsumerToken getAccessToken(OAuthConsumerToken requestToken, String verifier) throws OAuthRequestFailedException {
119 ProtectedResourceDetails details = getProtectedResourceDetailsService().loadProtectedResourceDetailsById(requestToken.getResourceId());
120 return getAccessToken(details, requestToken, verifier);
121 }
122
123 public OAuthConsumerTokenh/consumer/OAuthConsumerToken.html#OAuthConsumerToken">OAuthConsumerToken getAccessToken(ProtectedResourceDetails details, OAuthConsumerToken requestToken, String verifier) {
124 URL accessTokenURL;
125 try {
126 accessTokenURL = new URL(details.getAccessTokenURL());
127 }
128 catch (MalformedURLException e) {
129 throw new IllegalStateException("Malformed URL for obtaining an access token.", e);
130 }
131
132 String httpMethod = details.getAccessTokenHttpMethod();
133
134 Map<String, String> additionalParameters = new TreeMap<String, String>();
135 if (details.isUse10a()) {
136 if (verifier == null) {
137 throw new UnverifiedRequestTokenException("Unverified request token: " + requestToken);
138 }
139 additionalParameters.put(OAuthConsumerParameter.oauth_verifier.toString(), verifier);
140 }
141 Map<String, String> specifiedParams = details.getAdditionalParameters();
142 if (specifiedParams != null) {
143 additionalParameters.putAll(specifiedParams);
144 }
145 return getTokenFromProvider(details, accessTokenURL, httpMethod, requestToken, additionalParameters);
146 }
147
148
149 public InputStream readProtectedResource(URL url, OAuthConsumerToken accessToken, String httpMethod) throws OAuthRequestFailedException {
150 if (accessToken == null) {
151 throw new OAuthRequestFailedException("A valid access token must be supplied.");
152 }
153
154 ProtectedResourceDetails resourceDetails = getProtectedResourceDetailsService().loadProtectedResourceDetailsById(accessToken.getResourceId());
155 if ((!resourceDetails.isAcceptsAuthorizationHeader()) && !"POST".equalsIgnoreCase(httpMethod) && !"PUT".equalsIgnoreCase(httpMethod)) {
156 throw new IllegalArgumentException("Protected resource " + resourceDetails.getId() + " cannot be accessed with HTTP method " +
157 httpMethod + " because the OAuth provider doesn't accept the OAuth Authorization header.");
158 }
159
160 return readResource(resourceDetails, url, httpMethod, accessToken, resourceDetails.getAdditionalParameters(), null);
161 }
162
163
164
165
166
167
168
169
170
171
172
173
174 protected InputStream readResource(ProtectedResourceDetails details, URL url, String httpMethod, OAuthConsumerToken token, Map<String, String> additionalParameters, Map<String, String> additionalRequestHeaders) {
175 url = configureURLForProtectedAccess(url, token, details, httpMethod, additionalParameters);
176 String realm = details.getAuthorizationHeaderRealm();
177 boolean sendOAuthParamsInRequestBody = !details.isAcceptsAuthorizationHeader() && (("POST".equalsIgnoreCase(httpMethod) || "PUT".equalsIgnoreCase(httpMethod)));
178 HttpURLConnection connection = openConnection(url);
179
180 try {
181 connection.setRequestMethod(httpMethod);
182 }
183 catch (ProtocolException e) {
184 throw new IllegalStateException(e);
185 }
186
187 Map<String, String> reqHeaders = details.getAdditionalRequestHeaders();
188 if (reqHeaders != null) {
189 for (Map.Entry<String, String> requestHeader : reqHeaders.entrySet()) {
190 connection.setRequestProperty(requestHeader.getKey(), requestHeader.getValue());
191 }
192 }
193
194 if (additionalRequestHeaders != null) {
195 for (Map.Entry<String, String> requestHeader : additionalRequestHeaders.entrySet()) {
196 connection.setRequestProperty(requestHeader.getKey(), requestHeader.getValue());
197 }
198 }
199
200 int responseCode;
201 String responseMessage;
202 OutputStream out = null;
203 try {
204 connection.setDoOutput(sendOAuthParamsInRequestBody);
205 connection.connect();
206 if (sendOAuthParamsInRequestBody) {
207 String queryString = getOAuthQueryString(details, token, url, httpMethod, additionalParameters);
208 out = connection.getOutputStream();
209 out.write(queryString.getBytes("UTF-8"));
210 out.flush();
211 }
212 responseCode = connection.getResponseCode();
213 responseMessage = connection.getResponseMessage();
214 if (responseMessage == null) {
215 responseMessage = "Unknown Error";
216 }
217 }
218 catch (IOException e) {
219 throw new OAuthRequestFailedException("OAuth connection failed.", e);
220 }
221 finally {
222 try {
223 if (out != null) {
224 out.close();
225 }
226 }
227 catch (IOException e) {
228 logger.warn("Cannot close open stream: ", e);
229 }
230 }
231
232 if (responseCode >= 200 && responseCode < 300) {
233 try {
234 return connection.getInputStream();
235 }
236 catch (IOException e) {
237 throw new OAuthRequestFailedException("Unable to get the input stream from a successful response.", e);
238 }
239 }
240 else if (responseCode == 400) {
241 throw new OAuthRequestFailedException("OAuth authentication failed: " + responseMessage);
242 }
243 else if (responseCode == 401) {
244 String authHeaderValue = connection.getHeaderField("WWW-Authenticate");
245 if (authHeaderValue != null) {
246 Map<String, String> headerEntries = StringSplitUtils.splitEachArrayElementAndCreateMap(StringSplitUtils.splitIgnoringQuotes(authHeaderValue, ','), "=", "\"");
247 String requiredRealm = headerEntries.get("realm");
248 if ((requiredRealm != null) && (!requiredRealm.equals(realm))) {
249 throw new InvalidOAuthRealmException(String.format("Invalid OAuth realm. Provider expects \"%s\", when the resource details specify \"%s\".", requiredRealm, realm), requiredRealm);
250 }
251 }
252
253 throw new OAuthRequestFailedException("OAuth authentication failed: " + responseMessage);
254 }
255 else {
256 throw new OAuthRequestFailedException(String.format("Invalid response code %s (%s).", responseCode, responseMessage));
257 }
258 }
259
260
261
262
263
264
265
266
267
268
269
270
271 public URL configureURLForProtectedAccess(URL url, OAuthConsumerToken accessToken, String httpMethod, Map<String, String> additionalParameters) throws OAuthRequestFailedException {
272 return configureURLForProtectedAccess(url, accessToken, getProtectedResourceDetailsService().loadProtectedResourceDetailsById(accessToken.getResourceId()), httpMethod, additionalParameters);
273 }
274
275
276
277
278
279
280
281
282
283
284
285 protected URL configureURLForProtectedAccess(URL url, OAuthConsumerToken requestToken, ProtectedResourceDetails details, String httpMethod, Map<String, String> additionalParameters) {
286 String file;
287 if (!"POST".equalsIgnoreCase(httpMethod) && !"PUT".equalsIgnoreCase(httpMethod) && !details.isAcceptsAuthorizationHeader()) {
288 StringBuilder fileb = new StringBuilder(url.getPath());
289 String queryString = getOAuthQueryString(details, requestToken, url, httpMethod, additionalParameters);
290 fileb.append('?').append(queryString);
291 file = fileb.toString();
292 }
293 else {
294 file = url.getFile();
295 }
296
297 try {
298 if ("http".equalsIgnoreCase(url.getProtocol())) {
299 URLStreamHandler streamHandler = getStreamHandlerFactory().getHttpStreamHandler(details, requestToken, this, httpMethod, additionalParameters);
300 return new URL(url.getProtocol(), url.getHost(), url.getPort(), file, streamHandler);
301 }
302 else if ("https".equalsIgnoreCase(url.getProtocol())) {
303 URLStreamHandler streamHandler = getStreamHandlerFactory().getHttpsStreamHandler(details, requestToken, this, httpMethod, additionalParameters);
304 return new URL(url.getProtocol(), url.getHost(), url.getPort(), file, streamHandler);
305 }
306 else {
307 throw new OAuthRequestFailedException("Unsupported OAuth protocol: " + url.getProtocol());
308 }
309 }
310 catch (MalformedURLException e) {
311 throw new IllegalStateException(e);
312 }
313 }
314
315
316 public String getAuthorizationHeader(ProtectedResourceDetails details, OAuthConsumerToken accessToken, URL url, String httpMethod, Map<String, String> additionalParameters) {
317 if (!details.isAcceptsAuthorizationHeader()) {
318 return null;
319 }
320 else {
321 Map<String, Set<CharSequence>> oauthParams = loadOAuthParameters(details, url, accessToken, httpMethod, additionalParameters);
322 String realm = details.getAuthorizationHeaderRealm();
323
324 StringBuilder builder = new StringBuilder("OAuth ");
325 boolean writeComma = false;
326 if (realm != null) {
327 builder.append("realm=\"").append(realm).append('"');
328 writeComma = true;
329 }
330
331 for (Map.Entry<String, Set<CharSequence>> paramValuesEntry : oauthParams.entrySet()) {
332 Set<CharSequence> paramValues = paramValuesEntry.getValue();
333 CharSequence paramValue = findValidHeaderValue(paramValues);
334 if (paramValue != null) {
335 if (writeComma) {
336 builder.append(", ");
337 }
338
339 builder.append(paramValuesEntry.getKey()).append("=\"").append(oauthEncode(paramValue.toString())).append('"');
340 writeComma = true;
341 }
342 }
343
344 return builder.toString();
345 }
346 }
347
348
349
350
351
352
353
354 protected String findValidHeaderValue(Set<CharSequence> paramValues) {
355 String selectedValue = null;
356 if (paramValues != null && !paramValues.isEmpty()) {
357 CharSequence value = paramValues.iterator().next();
358 if (!(value instanceof QueryParameterValue)) {
359 selectedValue = value.toString();
360 }
361 }
362 return selectedValue;
363 }
364
365
366 public String getOAuthQueryString(ProtectedResourceDetails details, OAuthConsumerToken accessToken, URL url, String httpMethod, Map<String, String> additionalParameters) {
367 Map<String, Set<CharSequence>> oauthParams = loadOAuthParameters(details, url, accessToken, httpMethod, additionalParameters);
368
369 StringBuilder queryString = new StringBuilder();
370 if (details.isAcceptsAuthorizationHeader()) {
371
372 for (OAuthConsumerParameter oauthParam : OAuthConsumerParameter.values()) {
373 oauthParams.remove(oauthParam.toString());
374 }
375
376 if (additionalParameters != null) {
377 for (String additionalParam : additionalParameters.keySet()) {
378 oauthParams.remove(additionalParam);
379 }
380 }
381 }
382
383 Iterator<String> parametersIt = oauthParams.keySet().iterator();
384 while (parametersIt.hasNext()) {
385 String parameter = parametersIt.next();
386 queryString.append(parameter);
387 Set<CharSequence> values = oauthParams.get(parameter);
388 if (values != null) {
389 Iterator<CharSequence> valuesIt = values.iterator();
390 while (valuesIt.hasNext()) {
391 CharSequence parameterValue = valuesIt.next();
392 if (parameterValue != null) {
393 queryString.append('=').append(urlEncode(parameterValue.toString()));
394 }
395 if (valuesIt.hasNext()) {
396 queryString.append('&').append(parameter);
397 }
398 }
399 }
400 if (parametersIt.hasNext()) {
401 queryString.append('&');
402 }
403 }
404
405 return queryString.toString();
406 }
407
408
409
410
411
412
413
414
415
416
417
418
419 protected OAuthConsumerToken getTokenFromProvider(ProtectedResourceDetails details, URL tokenURL, String httpMethod,
420 OAuthConsumerToken requestToken, Map<String, String> additionalParameters) {
421 boolean isAccessToken = requestToken != null;
422 if (!isAccessToken) {
423
424 requestToken = new OAuthConsumerToken();
425 }
426
427 TreeMap<String, String> requestHeaders = new TreeMap<String, String>();
428 if ("POST".equalsIgnoreCase(httpMethod)) {
429 requestHeaders.put("Content-Type", "application/x-www-form-urlencoded");
430 }
431 InputStream inputStream = readResource(details, tokenURL, httpMethod, requestToken, additionalParameters, requestHeaders);
432 String tokenInfo;
433 try {
434 ByteArrayOutputStream out = new ByteArrayOutputStream();
435 byte[] buffer = new byte[1024];
436 int len = inputStream.read(buffer);
437 while (len >= 0) {
438 out.write(buffer, 0, len);
439 len = inputStream.read(buffer);
440 }
441
442 tokenInfo = new String(out.toByteArray(), "UTF-8");
443 }
444 catch (IOException e) {
445 throw new OAuthRequestFailedException("Unable to read the token.", e);
446 }
447 finally {
448 try {
449 if (inputStream != null) {
450 inputStream.close();
451 }
452 }
453 catch (IOException e) {
454 logger.warn("Cannot close open stream: ", e);
455 }
456 }
457
458 StringTokenizer tokenProperties = new StringTokenizer(tokenInfo, "&");
459 Map<String, String> tokenPropertyValues = new TreeMap<String, String>();
460 while (tokenProperties.hasMoreElements()) {
461 try {
462 String tokenProperty = (String) tokenProperties.nextElement();
463 int equalsIndex = tokenProperty.indexOf('=');
464 if (equalsIndex > 0) {
465 String propertyName = OAuthCodec.oauthDecode(tokenProperty.substring(0, equalsIndex));
466 String propertyValue = OAuthCodec.oauthDecode(tokenProperty.substring(equalsIndex + 1));
467 tokenPropertyValues.put(propertyName, propertyValue);
468 }
469 else {
470 tokenProperty = OAuthCodec.oauthDecode(tokenProperty);
471 tokenPropertyValues.put(tokenProperty, null);
472 }
473 }
474 catch (DecoderException e) {
475 throw new OAuthRequestFailedException("Unable to decode token parameters.");
476 }
477 }
478
479 String tokenValue = tokenPropertyValues.remove(OAuthProviderParameter.oauth_token.toString());
480 if (tokenValue == null) {
481 throw new OAuthRequestFailedException("OAuth provider failed to return a token.");
482 }
483
484 String tokenSecret = tokenPropertyValues.remove(OAuthProviderParameter.oauth_token_secret.toString());
485 if (tokenSecret == null) {
486 throw new OAuthRequestFailedException("OAuth provider failed to return a token secret.");
487 }
488
489 OAuthConsumerTokener/OAuthConsumerToken.html#OAuthConsumerToken">OAuthConsumerToken consumerToken = new OAuthConsumerToken();
490 consumerToken.setValue(tokenValue);
491 consumerToken.setSecret(tokenSecret);
492 consumerToken.setResourceId(details.getId());
493 consumerToken.setAccessToken(isAccessToken);
494 if (!tokenPropertyValues.isEmpty()) {
495 consumerToken.setAdditionalParameters(tokenPropertyValues);
496 }
497 return consumerToken;
498 }
499
500
501
502
503
504
505
506
507
508
509
510
511 protected Map<String, Set<CharSequence>> loadOAuthParameters(ProtectedResourceDetails details, URL requestURL, OAuthConsumerToken requestToken, String httpMethod, Map<String, String> additionalParameters) {
512 Map<String, Set<CharSequence>> oauthParams = new TreeMap<String, Set<CharSequence>>();
513
514 if (additionalParameters != null) {
515 for (Map.Entry<String, String> additionalParam : additionalParameters.entrySet()) {
516 Set<CharSequence> values = oauthParams.get(additionalParam.getKey());
517 if (values == null) {
518 values = new HashSet<CharSequence>();
519 oauthParams.put(additionalParam.getKey(), values);
520 }
521 if (additionalParam.getValue() != null) {
522 values.add(additionalParam.getValue());
523 }
524 }
525 }
526
527 String query = requestURL.getQuery();
528 if (query != null) {
529 StringTokenizer queryTokenizer = new StringTokenizer(query, "&");
530 while (queryTokenizer.hasMoreElements()) {
531 String token = (String) queryTokenizer.nextElement();
532 CharSequence value = null;
533 int equalsIndex = token.indexOf('=');
534 if (equalsIndex < 0) {
535 token = urlDecode(token);
536 }
537 else {
538 value = new QueryParameterValue(urlDecode(token.substring(equalsIndex + 1)));
539 token = urlDecode(token.substring(0, equalsIndex));
540 }
541
542 Set<CharSequence> values = oauthParams.get(token);
543 if (values == null) {
544 values = new HashSet<CharSequence>();
545 oauthParams.put(token, values);
546 }
547 if (value != null) {
548 values.add(value);
549 }
550 }
551 }
552
553 String tokenSecret = requestToken == null ? null : requestToken.getSecret();
554 String nonce = getNonceFactory().generateNonce();
555 oauthParams.put(OAuthConsumerParameter.oauth_consumer_key.toString(), Collections.singleton((CharSequence) details.getConsumerKey()));
556 if ((requestToken != null) && (requestToken.getValue() != null)) {
557 oauthParams.put(OAuthConsumerParameter.oauth_token.toString(), Collections.singleton((CharSequence) requestToken.getValue()));
558 }
559
560 oauthParams.put(OAuthConsumerParameter.oauth_nonce.toString(), Collections.singleton((CharSequence) nonce));
561 oauthParams.put(OAuthConsumerParameter.oauth_signature_method.toString(), Collections.singleton((CharSequence) details.getSignatureMethod()));
562 oauthParams.put(OAuthConsumerParameter.oauth_timestamp.toString(), Collections.singleton((CharSequence) String.valueOf(System.currentTimeMillis() / 1000)));
563 oauthParams.put(OAuthConsumerParameter.oauth_version.toString(), Collections.singleton((CharSequence) "1.0"));
564 String signatureBaseString = getSignatureBaseString(oauthParams, requestURL, httpMethod);
565 OAuthSignatureMethod signatureMethod;
566 try {
567 signatureMethod = getSignatureFactory().getSignatureMethod(details.getSignatureMethod(), details.getSharedSecret(), tokenSecret);
568 }
569 catch (UnsupportedSignatureMethodException e) {
570 throw new OAuthRequestFailedException(e.getMessage(), e);
571 }
572 String signature = signatureMethod.sign(signatureBaseString);
573 oauthParams.put(OAuthConsumerParameter.oauth_signature.toString(), Collections.singleton((CharSequence) signature));
574 return oauthParams;
575 }
576
577
578
579
580
581
582
583 protected String urlEncode(String value) {
584 try {
585 return URLEncoder.encode(value, "UTF-8");
586 }
587 catch (UnsupportedEncodingException e) {
588 throw new RuntimeException(e);
589 }
590 }
591
592
593
594
595
596
597
598 protected String urlDecode(String token) {
599 try {
600 return URLDecoder.decode(token, "utf-8");
601 }
602 catch (UnsupportedEncodingException e) {
603 throw new RuntimeException(e);
604 }
605 }
606
607
608
609
610
611
612
613 protected HttpURLConnection openConnection(URL requestTokenURL) {
614 try {
615 HttpURLConnection connection = (HttpURLConnection) requestTokenURL.openConnection(selectProxy(requestTokenURL));
616 connection.setConnectTimeout(getConnectionTimeout());
617 connection.setReadTimeout(getReadTimeout());
618 return connection;
619 }
620 catch (IOException e) {
621 throw new OAuthRequestFailedException("Failed to open an OAuth connection.", e);
622 }
623 }
624
625
626
627
628
629
630
631 protected Proxy selectProxy(URL requestTokenURL) {
632 try {
633 List<Proxy> selectedProxies = getProxySelector().select(requestTokenURL.toURI());
634 return selectedProxies.isEmpty() ? Proxy.NO_PROXY : selectedProxies.get(0);
635 }
636 catch (URISyntaxException e) {
637 throw new IllegalArgumentException(e);
638 }
639 }
640
641
642
643
644
645
646
647
648
649 protected String getSignatureBaseString(Map<String, Set<CharSequence>> oauthParams, URL requestURL, String httpMethod) {
650 TreeMap<String, TreeSet<String>> sortedParameters = new TreeMap<String, TreeSet<String>>();
651
652 for (Map.Entry<String, Set<CharSequence>> param : oauthParams.entrySet()) {
653
654 String key = oauthEncode(param.getKey());
655
656
657 TreeSet<String> sortedValues = sortedParameters.get(key);
658 if (sortedValues == null) {
659 sortedValues = new TreeSet<String>();
660 sortedParameters.put(key, sortedValues);
661 }
662
663 for (CharSequence value : param.getValue()) {
664 sortedValues.add(oauthEncode(value.toString()));
665 }
666 }
667
668
669 StringBuilder queryString = new StringBuilder();
670 Iterator<Map.Entry<String, TreeSet<String>>> sortedIt = sortedParameters.entrySet().iterator();
671 while (sortedIt.hasNext()) {
672 Map.Entry<String, TreeSet<String>> sortedParameter = sortedIt.next();
673 for (Iterator<String> sortedParametersIterator = sortedParameter.getValue().iterator(); sortedParametersIterator.hasNext();) {
674 String parameterValue = sortedParametersIterator.next();
675 if (parameterValue == null) {
676 parameterValue = "";
677 }
678
679 queryString.append(sortedParameter.getKey()).append('=').append(parameterValue);
680 if (sortedIt.hasNext() || sortedParametersIterator.hasNext()) {
681 queryString.append('&');
682 }
683 }
684 }
685
686 StringBuilder url = new StringBuilder(requestURL.getProtocol().toLowerCase()).append("://").append(requestURL.getHost().toLowerCase());
687 if ((requestURL.getPort() >= 0) && (requestURL.getPort() != requestURL.getDefaultPort())) {
688 url.append(":").append(requestURL.getPort());
689 }
690 url.append(requestURL.getPath());
691
692 return new StringBuilder(httpMethod.toUpperCase()).append('&').append(oauthEncode(url.toString())).append('&').append(oauthEncode(queryString.toString())).toString();
693 }
694
695
696
697
698
699
700 public ProtectedResourceDetailsService getProtectedResourceDetailsService() {
701 return protectedResourceDetailsService;
702 }
703
704
705
706
707
708
709
710 @Autowired
711 public void setProtectedResourceDetailsService(ProtectedResourceDetailsService protectedResourceDetailsService) {
712 this.protectedResourceDetailsService = protectedResourceDetailsService;
713 }
714
715
716
717
718
719
720 public OAuthURLStreamHandlerFactory getStreamHandlerFactory() {
721 return streamHandlerFactory;
722 }
723
724
725
726
727
728
729 @Autowired (required = false)
730 public void setStreamHandlerFactory(OAuthURLStreamHandlerFactory streamHandlerFactory) {
731 this.streamHandlerFactory = streamHandlerFactory;
732 }
733
734
735
736
737
738
739 public NonceFactory getNonceFactory() {
740 return nonceFactory;
741 }
742
743
744
745
746
747
748 @Autowired (required = false)
749 public void setNonceFactory(NonceFactory nonceFactory) {
750 this.nonceFactory = nonceFactory;
751 }
752
753
754
755
756
757
758 public OAuthSignatureMethodFactory getSignatureFactory() {
759 return signatureFactory;
760 }
761
762
763
764
765
766
767 @Autowired (required = false)
768 public void setSignatureFactory(OAuthSignatureMethodFactory signatureFactory) {
769 this.signatureFactory = signatureFactory;
770 }
771
772
773
774
775
776
777 public ProxySelector getProxySelector() {
778 return proxySelector;
779 }
780
781
782
783
784
785
786 @Autowired (required = false)
787 public void setProxySelector(ProxySelector proxySelector) {
788 this.proxySelector = proxySelector;
789 }
790
791
792
793
794
795
796 public int getConnectionTimeout() {
797 return connectionTimeout;
798 }
799
800
801
802
803
804
805 public void setConnectionTimeout(int connectionTimeout) {
806 this.connectionTimeout = connectionTimeout;
807 }
808
809
810
811
812
813
814 public int getReadTimeout() {
815 return readTimeout;
816 }
817
818
819
820
821
822
823 public void setReadTimeout(int readTimeout) {
824 this.readTimeout = readTimeout;
825 }
826
827
828
829
830 public static class QueryParameterValue implements CharSequence {
831
832 private final String value;
833
834 public QueryParameterValue(String value) {
835 this.value = value;
836 }
837
838 public int length() {
839 return this.value.length();
840 }
841
842 public char charAt(int index) {
843 return this.value.charAt(index);
844 }
845
846 public CharSequence subSequence(int start, int end) {
847 return this.value.subSequence(start, end);
848 }
849
850 @Override
851 public String toString() {
852 return this.value;
853 }
854
855 @Override
856 public int hashCode() {
857 return this.value.hashCode();
858 }
859
860 @Override
861 public boolean equals(Object obj) {
862 return this.value.equals(obj);
863 }
864 }
865 }