实现OAuth1签名生成
能力一般,技术有限,不喜勿喷!!!
Flutter对接Netsuite RESTlet 接口,卡了一天终于解决了
同样的数据在ApiFox上就可以执行成功,而我一直是 Error Code: 403,在网上找了很多文章最终都是 oauth_signature 签名与ApiFox不一致,但这种问题一般只有两种情况:- 签名基串参数或排序不对
- 加密错误
排错后发现是参数不对导致签名不一致,POST请求方式,BaseUrl中queryParameters也需要加入签名参数中
Step 1: 收集参数
用于请求头Authorization参数
Map<String, String> parameters = {
'oauth_consumer_key': _consumerKey, // 消费者密钥
'oauth_token': _token, // 访问令牌
'oauth_signature_method': oauthSignatureMethod, // 签名方法
'oauth_timestamp': timestamp, // 时间戳
'oauth_nonce': nonce, // 随机字符串
'oauth_version': '1.0', // OAuth版本
};
合并OAuth参数和queryParameters参数,此数据只用于生成签名,我就是在这里栽了跟头,在网上找了很多文章都没有明确的说明请求url中的查询参数需要加入签名,在这里需要复制一个新的Map数据。
// 合并请求参数
Map<String, String> parametersWithQuery = Map.from(parameters) ..addAll(uri.queryParameters);
Step 2: 创建签名基字符串
static String _generateSignatureBaseString(
String method, String baseUrl, Map<String, String> parameters) {
// Step 1: 对参数排序
var sortedParams = parameters.entries.toList()
..sort((a, b) => a.key.compareTo(b.key)); // 按照参数键进行排序
// Step 2: 构造规范参数字符串
String paramString = sortedParams
.map((e) =>
'${Uri.encodeComponent(e.key)}=${Uri.encodeComponent(e.value)}') // 编码参数键和值
.join('&'); //拼接为参数字符串
// Step 3: 构造签名基字符串
return '$method&${Uri.encodeComponent(baseUrl)}&${Uri.encodeComponent(paramString)}'; // 返回签名基字符串
}
Step 3: 创建签名密钥
// 创建签名基础字符串
String signingKey ='${Uri.encodeComponent(_consumerSecret)}&${Uri.encodeComponent(_tokenSecret)}';
Step 4: 生成HMAC-SHA256签名
static String _generateHmacSha256(String data, String key) {
var hmac = Hmac(sha256, utf8.encode(key)); // 创建HMAC对象
var digest = hmac.convert(utf8.encode(data)); // 生成摘要
return base64.encode(digest.bytes); // 返回Base64编码的签名
}
}
Step 5: 创建Authorization头值
//返回Authorization头值
'OAuth realm="$_realm", ${parameters.entries.map((e) => '${e.key}="${Uri.encodeComponent(e.value)}"').join(',')}';
完整代码
class OAuth1 {
static const String _consumerKey = 'your consumerKey'; //消费者密钥
static const String _consumerSecret = 'your consumerSecret'; //消费者密钥的密钥
static const String _token = 'your token'; //访问令牌
static const String _tokenSecret = 'your tokenSecret'; //令牌密钥
static const String _realm = 'your realm'; //领域
// 生成OAuth签名
// [method] HTTP请求方法(GET、POST等)
// [url] 请求url
static String generateOAuthSignature({
required String method,
required String url,
String oauthSignatureMethod = 'HMAC-SHA256',
}) {
String timestamp =
(DateTime.now().millisecondsSinceEpoch / 1000).floor().toString();
String nonce = DateTime.now().millisecondsSinceEpoch.toString();
Uri uri = Uri.parse(url);
// Step 1: 收集参数
Map<String, String> parameters = {
'oauth_consumer_key': _consumerKey,
'oauth_token': _token,
'oauth_signature_method': oauthSignatureMethod,
'oauth_timestamp': timestamp,
'oauth_nonce': nonce,
'oauth_version': '1.0',
};
// Step 2: 合并OAuth参数和查询参数
Map<String, String> parametersWithQuery = Map.from(parameters)
..addAll(uri.queryParameters);
// Step 3: 创建签名基字符串
String baseString = _generateSignatureBaseString(
method, '${uri.origin}${uri.path}', parametersWithQuery);
// Step 4: 创建签名密钥
String signingKey =
'${Uri.encodeComponent(_consumerSecret)}&${Uri.encodeComponent(_tokenSecret)}';
// Step 5: 生成HMAC-SHA256签名
String signature = _generateHmacSha256(baseString, signingKey);
parameters['oauth_signature'] = signature;
// Step 6: 创建Authorization头值
return 'OAuth realm="$_realm", ${parameters.entries.map((e) => '${e.key}="${Uri.encodeComponent(e.value)}"').join(',')}';
}
// 创建签名基字符串
static String _generateSignatureBaseString(
String method, String baseUrl, Map<String, String> parameters) {
// Step 1: 对参数排序
var sortedParams = parameters.entries.toList()
..sort((a, b) => a.key.compareTo(b.key));
// Step 2: 构造规范参数字符串
String paramString = sortedParams
.map((e) =>
'${Uri.encodeComponent(e.key)}=${Uri.encodeComponent(e.value)}')
.join('&');
// Step 3: 构造签名基字符串
return '$method&${Uri.encodeComponent(baseUrl)}&${Uri.encodeComponent(paramString)}';
}
/// 生成HMAC-SHA256签名
static String _generateHmacSha256(String data, String key) {
var hmac = Hmac(sha256, utf8.encode(key));
var digest = hmac.convert(utf8.encode(data));
return base64.encode(digest.bytes);
}
}
最终
感谢阅读, 各路大神如有错误欢迎指出,愿意修改,或有更好的办法欢迎评论留言。