我曾尝试为我的Java编程思想实现OAuth,但不幸失败了。我不知道为什么,但是我的代码不起作用。每次运行程序时,都会引发IOException,其原因为“ java.io.IOException:服务器返回的HTTP响应代码:401”(401表示未授权)。我仔细查看了文档,但是我真的不明白为什么它不起作用。我想使用的OAuth提供程序是twitter,我也在其中注册了我的应用程序。提前致谢phineas
OAuth docsTwitter API wikiClass Base64Coder
import java.io.InputStreamReader;
import java.io.BufferedReader;
import java.io.OutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.net.URLEncoder;
import java.net.URLConnection;
import java.net.MalformedURLException;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.security.NoSuchAlgorithmException;
import java.security.InvalidKeyException;
public class Request {
public static String read(String url) {
StringBuffer buffer = new StringBuffer();
try {
/**
* get the time - note: value below zero
* the millisecond value is used for oauth_nonce later on
*/
int millis = (int) System.currentTimeMillis() * -1;
int time = (int) millis / 1000;
/**
* Listing of all parameters necessary to retrieve a token
* (sorted lexicographically as demanded)
*/
String[][] data = {
{"oauth_callback", "SOME_URL"},
{"oauth_consumer_key", "MY_CONSUMER_KEY"},
{"oauth_nonce", String.valueOf(millis)},
{"oauth_signature", ""},
{"oauth_signature_method", "HMAC-SHA1"},
{"oauth_timestamp", String.valueOf(time)},
{"oauth_version", "1.0"}
};
/**
* Generation of the signature base string
*/
String signature_base_string =
"POST&"+URLEncoder.encode(url, "UTF-8")+"&";
for(int i = 0; i < data.length; i++) {
// ignore the empty oauth_signature field
if(i != 3) {
signature_base_string +=
URLEncoder.encode(data[i][0], "UTF-8") + "%3D" +
URLEncoder.encode(data[i][1], "UTF-8") + "%26";
}
}
// cut the last appended %26
signature_base_string = signature_base_string.substring(0,
signature_base_string.length()-3);
/**
* Sign the request
*/
Mac m = Mac.getInstance("HmacSHA1");
m.init(new SecretKeySpec("CONSUMER_SECRET".getBytes(), "HmacSHA1"));
m.update(signature_base_string.getBytes());
byte[] res = m.doFinal();
String sig = String.valueOf(Base64Coder.encode(res));
data[3][1] = sig;
/**
* Create the header for the request
*/
String header = "OAuth ";
for(String[] item : data) {
header += item[0]+"=\""+item[1]+"\", ";
}
// cut off last appended comma
header = header.substring(0, header.length()-2);
System.out.println("Signature Base String: "+signature_base_string);
System.out.println("Authorization Header: "+header);
System.out.println("Signature: "+sig);
String charset = "UTF-8";
URLConnection connection = new URL(url).openConnection();
connection.setDoInput(true);
connection.setDoOutput(true);
connection.setRequestProperty("Accept-Charset", charset);
connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded;charset=" + charset);
connection.setRequestProperty("Authorization", header);
connection.setRequestProperty("User-Agent", "XXXX");
OutputStream output = connection.getOutputStream();
output.write(header.getBytes(charset));
BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
String read;
while((read = reader.readLine()) != null) {
buffer.append(read);
}
}
catch(Exception e) {
e.printStackTrace();
}
return buffer.toString();
}
public static void main(String[] args) {
System.out.println(Request.read("http://api.twitter.com/oauth/request_token"));
}
}
尝试删除参数“ oauth_callback”。它为我工作。我正在处理的应用程序是一个Web应用程序。
我知道这是一个古老的问题,但似乎需要深入研究才能最终找到正确的答案。这似乎也是Google的热门链接之一。 dev.twitter.com上的页面也几乎无处可寻。所以就到这里。在here中查找正确处理它的代码。它使用HttpCore
,但可以使用标准库来实现。
使故事简短。
就此代码而言。对于请求令牌,它似乎根本不重要。
connection.setRequestProperty("Accept-Charset", charset);
connection.setRequestProperty("Content-Type", "application/x-www-formurlencoded;charset="+ charset);
connection.setRequestProperty("User-Agent", "XXXX");
虽然这是connection.setRequestMethod(method);
[如果有人需要Twitter的oAuth签名和标头,则为以下代码。这使用Java 8,没有第3方库。
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.stream.Collectors;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
/**
* Class to generate Oauth 1.0a header for Twitter
*
*/
public class TwitterOauthHeaderGenerator {
private String consumerKey;
private String consumerSecret;
private String signatureMethod;
private String token;
private String tokenSecret;
private String version;
public TwitterOauthHeaderGenerator(String consumerKey, String consumerSecret, String token, String tokenSecret) {
this.consumerKey = consumerKey;
this.consumerSecret = consumerSecret;
this.token = token;
this.tokenSecret = tokenSecret;
this.signatureMethod = "HMAC-SHA1";
this.version = "1.0";
}
private static final String oauth_consumer_key = "oauth_consumer_key";
private static final String oauth_token = "oauth_token";
private static final String oauth_signature_method = "oauth_signature_method";
private static final String oauth_timestamp = "oauth_timestamp";
private static final String oauth_nonce = "oauth_nonce";
private static final String oauth_version = "oauth_version";
private static final String oauth_signature = "oauth_signature";
private static final String HMAC_SHA1 = "HmacSHA1";
/**
* Generates oAuth 1.0a header which can be pass as Authorization header
*
* @param httpMethod
* @param url
* @param requestParams
* @return
*/
public String generateHeader(String httpMethod, String url, Map<String, String> requestParams) {
StringBuilder base = new StringBuilder();
String baseSignatureString = generateSignatureBaseString(httpMethod, url, requestParams);
String signature = encryptUsingHmacSHA1(baseSignatureString);
base.append("OAuth ");
append(base, oauth_consumer_key, consumerKey);
append(base, oauth_token, token);
append(base, oauth_signature_method, signatureMethod);
append(base, oauth_timestamp, getTimestamp());
append(base, oauth_nonce, getNonce());
append(base, oauth_version, version);
append(base, oauth_signature, signature);
base.deleteCharAt(base.length() - 1);
return base.toString();
}
/**
* Generate base string to generate the oauth_signature
*
* @param httpMethod
* @param url
* @param requestParams
* @return
*/
private String generateSignatureBaseString(String httpMethod, String url, Map<String, String> requestParams) {
Map<String, String> params = new HashMap<>();
requestParams.entrySet().forEach(entry -> {
put(params, entry.getKey(), entry.getValue());
});
put(params, oauth_consumer_key, consumerKey);
put(params, oauth_nonce, getNonce());
put(params, oauth_signature_method, signatureMethod);
put(params, oauth_timestamp, getTimestamp());
put(params, oauth_token, token);
put(params, oauth_version, version);
Map<String, String> sortedParams = params.entrySet().stream().sorted(Map.Entry.comparingByKey())
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (oldValue, newValue) -> oldValue, LinkedHashMap::new));
StringBuilder base = new StringBuilder();
sortedParams.entrySet().forEach(entry -> {
base.append(entry.getKey()).append("=").append(entry.getValue()).append("&");
});
base.deleteCharAt(base.length() - 1);
String baseString = httpMethod.toUpperCase() + "&" + encode(url) + "&" + encode(base.toString());
return baseString;
}
private String encryptUsingHmacSHA1(String input) {
String secret = new StringBuilder().append(encode(consumerSecret)).append("&").append(encode(tokenSecret)).toString();
byte[] keyBytes = secret.getBytes(StandardCharsets.UTF_8);
SecretKey key = new SecretKeySpec(keyBytes, HMAC_SHA1);
Mac mac;
try {
mac = Mac.getInstance(HMAC_SHA1);
mac.init(key);
} catch (NoSuchAlgorithmException | InvalidKeyException e) {
e.printStackTrace();
return null;
}
byte[] signatureBytes = mac.doFinal(input.getBytes(StandardCharsets.UTF_8));
return new String(Base64.getEncoder().encode(signatureBytes));
}
/**
* Percentage encode String as per RFC 3986, Section 2.1
*
* @param value
* @return
*/
private String encode(String value) {
String encoded = "";
try {
encoded = URLEncoder.encode(value, "UTF-8");
} catch (Exception e) {
e.printStackTrace();
}
String sb = "";
char focus;
for (int i = 0; i < encoded.length(); i++) {
focus = encoded.charAt(i);
if (focus == '*') {
sb += "%2A";
} else if (focus == '+') {
sb += "%20";
} else if (focus == '%' && i + 1 < encoded.length() && encoded.charAt(i + 1) == '7' && encoded.charAt(i + 2) == 'E') {
sb += '~';
i += 2;
} else {
sb += focus;
}
}
return sb.toString();
}
private void put(Map<String, String> map, String key, String value) {
map.put(encode(key), encode(value));
}
private void append(StringBuilder builder, String key, String value) {
builder.append(encode(key)).append("=\"").append(encode(value)).append("\",");
}
private String getNonce() {
return (System.currentTimeMillis() / 1000) + "wow";
}
private String getTimestamp() {
return (System.currentTimeMillis() / 1000) + "";
}
}
使用Spring RestTemplate来获取Twitter用户的详细信息的示例用法:
TwitterOauthHeaderGenerator generator = new TwitterOauthHeaderGenerator("consumerKey", "consumerSecret", "token", "tokenSecret");
Map<String, String> requestParams = new HashMap<>();
requestParams.put("usernames", "some_handle");
String header = generator.generateHeader("GET", "https://api.twitter.com/labs/1/users", requestParams);
HttpHeaders headers = new HttpHeaders();
headers.add("Authorization", header);
HttpEntity<String> httpEntity = new HttpEntity<String>("body", headers);
ResponseEntity<SomeModel> someModelEntity= restTemplate.exchange("https://api.twitter.com/labs/1/users?usernames=some_handle",
HttpMethod.GET, httpEntity, SomeModel.class);
System.out.println(someModelEntity.getBody());