问题描述
当我尝试通过Here APi获取oauth令牌时出现此错误
这是错误
wrong.","error":"invalid_client","error_description":"errorCode: '401300'. Signature mismatch. Authorization signature or client credential is wrong."}
这是我在Flutter中的oauth代码
import 'dart:async';
import 'dart:convert';
import 'dart:math';
import 'package:http/http.dart' as http;
import 'package:convert/convert.dart';
import 'package:crypto/crypto.dart';
import 'package:let_log/let_log.dart';
class Oauthv1 {
String oauthv1BaseUrl = "account.api.here.com";
bool isJson = false;
final String consumerKey,consumerKeySecret,accessToken,accessTokenSecret;
Hmac _sigHasher;
Oauthv1(this.consumerKey,this.consumerKeySecret,this.accessToken,this.accessTokenSecret) {
var bytes = utf8.encode("$consumerKeySecret");
_sigHasher = new Hmac(sha256,bytes);
}
Oauthv1 forceXml() {
this.isJson = false;
return this;
}
Future<http.Response> request(Map<String,String> data) {
if (isJson) {
data["format"] = "json";
}
return _callGetApi("oauth2/token",data);
}
Future<http.Response> _callGetApi(String url,Map<String,String> data) {
Uri requestUrl = Uri.https(oauthv1BaseUrl,url);
print(data["grant_type"]);
_setAuthParams("POST",requestUrl.toString(),data);
requestUrl = Uri.https(requestUrl.authority,requestUrl.path,data);
String oAuthHeader = _generateOAuthHeader(data);
Map<String,String> _headers = {
'Content-Type': 'application/x-www-form-urlencoded','Authorization': '$oAuthHeader'
};
//Logger.debug(_headers);
// Build the OAuth HTTP Header from the data.
// Build the form data (exclude OAuth stuff that's already in the header).
// var formData = _filterMap(data,(k) => !k.startsWith("oauth_"));
//Logger.debug(requestUrl);
//Logger.warn(data);
Logger.debug(_headers);
return _sendGetRequest(requestUrl,{'grant_type': 'client_credentials'},_headers);
}
void _setAuthParams(
String requestMethod,String url,String> data) {
// Timestamps are in seconds since 1/1/1970.
// var timestamp = new DateTime.now().toUtc().difference(_epochUtc).inSeconds;
/* var millisecondsSinceEpoch =
new DateTime.now().toUtc().millisecondsSinceEpoch;
var timestamp = (millisecondsSinceEpoch / 100).round(); */
var ms = (new DateTime.now()).millisecondsSinceEpoch;
var timestamp = (ms / 1000).round();
// Add all the OAuth headers we'll need to use when constructing the hash.
data["oauth_consumer_key"] = consumerKey;
data["oauth_signature_method"] = "HMAC-SHA256";
data["oauth_timestamp"] = timestamp.toString();
data["oauth_nonce"] =
_randomString(8); // Required,but Twitter doesn't appear to use it
if (accessToken != null && accessToken.isNotEmpty)
data["oauth_token"] = accessToken;
data["oauth_version"] = "1.0";
// Generate the OAuth signature and add it to our payload.
data["oauth_signature"] =
_generateSignature(requestMethod,Uri.parse(url),data);
}
/// Generate an OAuth signature from OAuth header values.
String _generateSignature(
String requestMethod,Uri url,String> data) {
var sigString = _toQueryString(data);
var fullSigData =
"$requestMethod&${_encode(url.toString())}&${_encode(sigString)}";
return base64.encode(_hash(fullSigData));
}
/// Generate the raw OAuth HTML header from the values (including signature).
String _generateOAuthHeader(Map<String,String> data) {
var oauthHeaderValues = _filterMap(data,(k) => k.startsWith("oauth_"));
return "OAuth " + _toOAuthHeader(oauthHeaderValues);
}
/// Send HTTP Request and return the response.
Future<http.Response> _sendGetRequest(Uri fullUrl,String> data,String> headers) async {
return await http.post("https://account.api.here.com/oauth2/token",body: 'grant_type=client_credentials',headers: headers);
}
Map<String,String> _filterMap(
Map<String,String> map,bool test(String key)) {
return new Map.fromIterable(map.keys.where(test),value: (k) => map[k]);
}
String _toQueryString(Map<String,String> data) {
var items = data.keys.map((k) => "$k=${_encode(data[k])}").toList();
items.sort();
return items.join("&");
}
String _toOAuthHeader(Map<String,String> data) {
var items = data.keys.map((k) => "$k=\"${_encode(data[k])}\"").toList();
items.sort();
return items.join(",");
}
List<int> _hash(String data) => _sigHasher.convert(data.codeUnits).bytes;
String _encode(String data) => percent.encode(data.codeUnits);
String _randomString(int length) {
var rand = new Random();
var codeUnits = new List.generate(length,(index) {
return rand.nextInt(26) + 97;
});
return new String.fromCharCodes(codeUnits);
}
}
我发送了所有标头和正文请求,但响应失败。正文请求是
grant_type = client_credentials
我真的不知道什么时候不发送哪些数据或错过了哪些参数。我正在使用Flutter Oauth 1.0。 auth在邮递员中可以正常工作,但在Flutter中不能集成。
当我从sendgetrequest()方法打印_headers时,它会打印以下内容: {内容类型:应用程序/ x-www-form-urlencoded,授权:OAuth oauth_consumer_key =“ o2zr ***** bXuA”,oauth_nonce =“ oaqpiovg”,oauth_signature =“ H7M92BoEneotellYHqJCMkMfLOq9sMrm1R5 %3D”, oauth_signature_method =“ HMAC-SHA256”,oauth_timestamp =“ 1601165843”,oauth_version =“ 1.0”}
您可以看到标题格式正确
解决方法
我通过follow step by step: Create OAuth 1.0 signature section on here Documentation and it worked fine
解决了这个问题问题是oauth参数中的顺序未正确遵循。