获得使用 Java 或 Groovy 填充的有效 oauth_signature 的绝对最少代码?
Posted
技术标签:
【中文标题】获得使用 Java 或 Groovy 填充的有效 oauth_signature 的绝对最少代码?【英文标题】:Absolute minimum code to get a valid oauth_signature populated in Java or Groovy? 【发布时间】:2011-04-14 22:48:39 【问题描述】:所以我正在测试一个 Rest OAuth 实现。 我的测试工具会发送 HTTP 请求,但我需要准备 Authorization 标头。
我需要什么:我想要一个有效的授权标头
我有什么:除 oauth_signature 之外的所有标头 我还有 2 个秘密,token_secret 和 consumer_secret。我还拥有access_token。所以它真的归结为,必须签署这个请求。我该怎么做?
总结:我只需要为 RESTful 服务填充 Authorization 标头的 oauth_signature 部分。我该怎么做?
基本上:
oAuthHeader="OAuth";
oAuthHeader=oAuthHeader+" oauth_signature_method="+oauth_signature_method;
oAuthHeader=oAuthHeader+",oauth_version="+oauth_version;
oAuthHeader=oAuthHeader+",oauth_nonce="+oauth_nonce;
oAuthHeader=oAuthHeader+",oauth_timestamp="+oauth_timestamp;
oAuthHeader=oAuthHeader+",oauth_consumer_key="+oauth_consumer_key;
oAuthHeader=oAuthHeader+",oauth_token="+oauth_token;
oAuthHeader=oAuthHeader+",oauth_signature="+**oauth_signature**;
Authorization = oAuthHeader;
我的问题是我没有它的 oauth_signature 部分。我不知道如何得到它。请帮忙?
【问题讨论】:
【参考方案1】:这是我的 Flickr OAuth 代码。注意:我引用了 SignPost 的一些逻辑。生成它的签名真的很棘手......好吧。这只是生成“oauth_signature”的示例
package oauthflickr;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URLEncoder;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.List;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.utils.URIUtils;
import org.apache.http.client.utils.URLEncodedUtils;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
/**
* a simple program to get flickr token and token secret.
*
* @author Mark Zang
*
*/
public class OAuthForFlickr
private static String key = "_________________________";
private static String secret = "___________";
private static final String HMAC_SHA1 = "HmacSHA1";
private static final String ENC = "UTF-8";
private static Base64 base64 = new Base64();
/**
*
* @param url
* the url for "request_token" URLEncoded.
* @param params
* parameters string, URLEncoded.
* @return
* @throws UnsupportedEncodingException
* @throws NoSuchAlgorithmException
* @throws InvalidKeyException
*/
private static String getSignature(String url, String params)
throws UnsupportedEncodingException, NoSuchAlgorithmException,
InvalidKeyException
/**
* base has three parts, they are connected by "&": 1) protocol 2) URL
* (need to be URLEncoded) 3) Parameter List (need to be URLEncoded).
*/
StringBuilder base = new StringBuilder();
base.append("GET&");
base.append(url);
base.append("&");
base.append(params);
System.out.println("Stirng for oauth_signature generation:" + base);
// yea, don't ask me why, it is needed to append a "&" to the end of
// secret key.
byte[] keyBytes = (secret + "&").getBytes(ENC);
SecretKey key = new SecretKeySpec(keyBytes, HMAC_SHA1);
Mac mac = Mac.getInstance(HMAC_SHA1);
mac.init(key);
// encode it, base64 it, change it to string and return.
return new String(base64.encode(mac.doFinal(base.toString().getBytes(
ENC))), ENC).trim();
/**
* @param args
* @throws IOException
* @throws ClientProtocolException
* @throws URISyntaxException
* @throws NoSuchAlgorithmException
* @throws InvalidKeyException
*/
public static void main(String[] args) throws ClientProtocolException,
IOException, URISyntaxException, InvalidKeyException,
NoSuchAlgorithmException
HttpClient httpclient = new DefaultHttpClient();
List<NameValuePair> qparams = new ArrayList<NameValuePair>();
// These params should ordered in key
qparams.add(new BasicNameValuePair("oauth_callback", "oob"));
qparams.add(new BasicNameValuePair("oauth_consumer_key", key));
qparams.add(new BasicNameValuePair("oauth_nonce", ""
+ (int) (Math.random() * 100000000)));
qparams.add(new BasicNameValuePair("oauth_signature_method",
"HMAC-SHA1"));
qparams.add(new BasicNameValuePair("oauth_timestamp", ""
+ (System.currentTimeMillis() / 1000)));
qparams.add(new BasicNameValuePair("oauth_version", "1.0"));
// generate the oauth_signature
String signature = getSignature(URLEncoder.encode(
"http://www.flickr.com/services/oauth/request_token", ENC),
URLEncoder.encode(URLEncodedUtils.format(qparams, ENC), ENC));
// add it to params list
qparams.add(new BasicNameValuePair("oauth_signature", signature));
// generate URI which lead to access_token and token_secret.
URI uri = URIUtils.createURI("http", "www.flickr.com", -1,
"/services/oauth/request_token",
URLEncodedUtils.format(qparams, ENC), null);
System.out.println("Get Token and Token Secrect from:"
+ uri.toString());
HttpGet httpget = new HttpGet(uri);
// output the response content.
System.out.println("oken and Token Secrect:");
HttpResponse response = httpclient.execute(httpget);
HttpEntity entity = response.getEntity();
if (entity != null)
InputStream instream = entity.getContent();
int len;
byte[] tmp = new byte[2048];
while ((len = instream.read(tmp)) != -1)
System.out.println(new String(tmp, 0, len, ENC));
【讨论】:
这是我两天奋斗的救赎。谢谢。 太棒了!帮助了我 有时服务器将回调方法视为额外参数并抛出签名不匹配错误。删除回调对我有用。我正在测试 WordPress OAuth1.0a 插件。 不,事实上,您的“不要问我为什么需要附加 &”的答案可以在这里找到:oauth1.wp-api.org/docs/basics/Signing.html:秘密由客户端秘密和令牌组成秘密。当你没有令牌秘密时,你需要 client_secret&,当你有两者时,你需要 client_secret&token_secret。【参考方案2】:对于 Twitter oAuth: 如果有人需要生成 oAuth 签名和标头以连接到 Twitter API,这里是代码。这至少需要 Java 8 和 NO 3rd 方库。
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Random;
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 passed 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 nonce = getNonce();
String timestamp = getTimestamp();
String baseSignatureString = generateSignatureBaseString(httpMethod, url, requestParams, nonce, timestamp);
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, timestamp);
append(base, oauth_nonce, nonce);
append(base, oauth_version, version);
append(base, oauth_signature, signature);
base.deleteCharAt(base.length() - 1);
System.out.println("header : " + base.toString());
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, String nonce, String timestamp)
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, nonce);
put(params, oauth_signature_method, signatureMethod);
put(params, oauth_timestamp, timestamp);
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()
int leftLimit = 48; // numeral '0'
int rightLimit = 122; // letter 'z'
int targetStringLength = 10;
Random random = new Random();
String generatedString = random.ints(leftLimit, rightLimit + 1).filter(i -> (i <= 57 || i >= 65) && (i <= 90 || i >= 97)).limit(targetStringLength)
.collect(StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append).toString();
return generatedString;
private String getTimestamp()
return Math.round((new Date()).getTime() / 1000.0) + "";
使用 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());
完整的代码和工作演示可在Twitter-Play获得
【讨论】:
太棒了。这正是我想要的 谢谢你救了我一整天。不如写一篇博文让更多人受益 @MohamedDaher 我没有尝试过使用 NodeJS,但您可以使用twitter-guide 中提到的步骤尝试一下,如果遇到任何问题,请在此处提问 尽我所能在我自己的代码库 (imgur.com/a/itU5eHR) 中为您提供信任。你应该考虑把它放在一个 repo 中,并将它连接到 JitPack (jitpack.io) 或 GitHub Packages 之类的东西上,这样人们就可以将它用作工件。太棒了。 @MattWelke 很高兴这个答案对您有所帮助。我对你或其他任何人使用任何许可证的代码都很好。【参考方案3】:对于带有 JIRA 的 OAuth 1.0a。我能够从上面的 Twitter 示例中受益,但对于 JIRA,我必须使用 RSA-SHA1 而不是 HMACSHA1 对生成的SignatureBaseString 进行数字签名。
以下是使用 RSA-SHA1 进行签名的 sn-ps。这是在 Groovy 中,使用 Java 8,没有任何外部库:
import java.security.SignatureException;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.security.Signature;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
//-------- generate RSA-SHA1 signature from request data
def encryptUsingRSASHA1(String data, String key) throws
java.security.SignatureException
String result
try
// get an rsa_sha1 key from the raw key bytes
//SHA1withRSA
Signature signature = Signature.getInstance("SHA1withRSA");
signature.initSign(getPrivateKey(key));
signature.update(data.getBytes("UTF-8"));
byte[] rawRSAsigned = signature.sign();
result= rawRSAsigned.encodeBase64()
catch (Exception e)
throw new SignatureException("Failed to generate Signature : " +
e.getMessage());
return result
//get PrivateKey from key string
def getPrivateKey(String privateKey) throws NoSuchAlgorithmException,
InvalidKeySpecException
String privateKeyPEM = privateKey.replace("-----BEGIN PRIVATE KEY-----\n",
"").replace("-----END PRIVATE KEY-----", "").replaceAll("\n","");
byte[] privateKeyBytes = privateKeyPEM.decodeBase64();
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKeyBytes);
KeyFactory kf = KeyFactory.getInstance("RSA");
return kf.generatePrivate(keySpec);
【讨论】:
以上是关于获得使用 Java 或 Groovy 填充的有效 oauth_signature 的绝对最少代码?的主要内容,如果未能解决你的问题,请参考以下文章
如何自动将 Java 代码转换为 Groovy 代码 [关闭]