来自 swift iOS 的 Netsuite Oauth 1.0 身份验证
Posted
技术标签:
【中文标题】来自 swift iOS 的 Netsuite Oauth 1.0 身份验证【英文标题】:Netsuite Oauth 1.0 authentication from swift iOS 【发布时间】:2021-05-12 18:21:36 【问题描述】:我正在尝试将 netsuite 模块集成到 ios 应用程序中。我无法为标头生成正确的 oauth 签名。为此,我尝试使用“OAuthSwift”库,但我不知道如何添加像 oauth_nonce、oauth_signature 这样的标头。我搜索了许多网站并检查了 *** 引用,但没有找到符合我要求的。
根据评论,我遵循了发布的 java 代码 [https://***.com/questions/51286409/netsuite-token-based-authentication]。
这是我处理过的代码。我得到的响应是 INVALID_LOGIN_ATTEMPT。我的猜测是,也许 OAUTH_Signature 是错误的。请帮帮我。
import UIKit
import CommonCrypto
class ViewController: UIViewController
var OAuth = "";
override func viewDidLoad()
super.viewDidLoad()
var oauth_val = mediate();
let url = URL(string: "https://rest.na1.netsuite.com/app/site/hosting/restlet.nl?script=xxx&deploy=x")
guard let requestUrl = url else fatalError()
// Create URL Request
var request = URLRequest(url: requestUrl)
// Specify HTTP Method to use
request.httpMethod = "GET"
//http header
request.setValue("application/json", forHTTPHeaderField: "Accept")
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.setValue(oauth_val, forHTTPHeaderField: "Authorization")
// Send HTTP Request
let task = URLSession.shared.dataTask(with: request) (data, response, error) in
// Check if Error took place
if let error = error
print("Error took place \(error)")
return
// Read HTTP Response Status code
if let response = response as? HTTPURLResponse
print("Response HTTP Status code: \(response.statusCode)")
// Convert HTTP Response Data to a simple String
if let data = data, let dataString = String(data: data, encoding: .utf8)
print("Response data string:\n \(dataString)")
task.resume()
func randomString(length: Int) -> String
let letters = "2312312312312sadadadadNKSNSKMSLMXSX"
return String((0..<length).map _ in letters.randomElement()! )
func computeSignature(baseString: String, keyString: String) -> String
let hmacResult:String = keyString.hmac(algorithm: HMACAlgorithm.SHA1, key: keyString)
return hmacResult
func mediate()-> String
let base_url = "https://rest.na1.netsuite.com/app/site/hosting/restlet.nl?"
let http_method = "GET"
var token_id = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
var token_secret = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
var consumerkey = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
var consumer_secret = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
var SIGNATURE_METHOD = "HMAC-SHA1"
var OAUTH_NONCE = randomString(length: 20)
var TIME_STAMP = String(Date().toMillis())
var OAUTH_VERSION = "1.0"
var SCRIPT_DEPLOYMENT_ID = "xxx"
var SCRIPT_ID = "xxx"
var REALM = "xxxxx"
var encdata = ""
encdata = encdata + "deploy=" + SCRIPT_DEPLOYMENT_ID + "&"
encdata = encdata + "oauth_consumer_key=" + consumerkey + "&"
encdata = encdata + "oauth_nonce=" + OAUTH_NONCE + "&"
encdata = encdata + "oauth_signature_method=" + SIGNATURE_METHOD + "&"
encdata = encdata + "oauth_timestamp=" + TIME_STAMP + "&"
encdata = encdata + "oauth_token=" + token_id + "&"
encdata = encdata + "oauth_version=" + OAUTH_VERSION + "&"
encdata = encdata + "script=" + SCRIPT_ID
let encodeData = encdata.urlEncoded()!
let completeData = http_method + "&" + base_url.urlEncoded()! + "&" + encodeData
var key = "";
key = consumer_secret.urlEncoded()! + "&" + token_secret.urlEncoded()!
var signature = computeSignature(baseString: completeData, keyString: key)
OAuth = "OAuth realm=\"" + REALM + "\",";
OAuth = OAuth + "oauth_consumer_key=\"" + consumerkey + "\",";
OAuth = OAuth + "oauth_token=\"" + token_id + "\",";
OAuth = OAuth + "oauth_signature_method=\"HMAC-SHA1\",";
OAuth = OAuth + "oauth_timestamp=\"" + TIME_STAMP + "\",";
OAuth = OAuth + "oauth_nonce=\"" + OAUTH_NONCE + "\",";
OAuth = OAuth + "oauth_version=\"" + "1.0" + "\",";
OAuth = OAuth + "oauth_signature=\"" + signature + "\"";
return OAuth;
func printCharactersInSet(set: NSCharacterSet)
var characters = ""
let iSet = set.inverted
for i: UInt32 in 32..<127
let c = Character(UnicodeScalar(UInt32(i))!)
if iSet.hasMember(inPlane: UInt8(i))
characters = characters + String(c)
print("characters not in set: \'\(characters)\'")
enum HMACAlgorithm
case MD5, SHA1, SHA224, SHA256, SHA384, SHA512
func toCCHmacAlgorithm() -> CCHmacAlgorithm
var result: Int = 0
switch self
case .MD5:
result = kCCHmacAlgMD5
case .SHA1:
result = kCCHmacAlgSHA1
case .SHA224:
result = kCCHmacAlgSHA224
case .SHA256:
result = kCCHmacAlgSHA256
case .SHA384:
result = kCCHmacAlgSHA384
case .SHA512:
result = kCCHmacAlgSHA512
return CCHmacAlgorithm(result)
func digestLength() -> Int
var result: CInt = 0
switch self
case .MD5:
result = CC_MD5_DIGEST_LENGTH
case .SHA1:
result = CC_SHA1_DIGEST_LENGTH
case .SHA224:
result = CC_SHA224_DIGEST_LENGTH
case .SHA256:
result = CC_SHA256_DIGEST_LENGTH
case .SHA384:
result = CC_SHA384_DIGEST_LENGTH
case .SHA512:
result = CC_SHA512_DIGEST_LENGTH
return Int(result)
extension String
func hmac(algorithm: HMACAlgorithm, key: String) -> String
let cKey = key.cString(using: String.Encoding.utf8)
let cData = self.cString(using: String.Encoding.utf8)
var result = [CUnsignedChar](repeating: 0, count: Int(algorithm.digestLength()))
CCHmac(algorithm.toCCHmacAlgorithm(), cKey!, strlen(cKey!), cData!, strlen(cData!), &result)
let hmacData:NSData = NSData(bytes: result, length: (Int(algorithm.digestLength())))
let hmacBase64 = hmacData.base64EncodedString(options: NSData.Base64EncodingOptions.lineLength76Characters)// Encoding76CharacterLineLength)
return String(hmacBase64)
var urlEncoded: String
let customAllowedSet = CharacterSet(charactersIn: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._~")
return self.addingPercentEncoding(withAllowedCharacters: customAllowedSet)!
var urlQueryEncoded: String?
return self.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed)
func substring(to offset: String.IndexDistance) -> String
let to = self.index(self.startIndex, offsetBy: offset)
return String(self[..<to])
extension Dictionary
var urlEncodedQuery: String
var parts = [String]()
for (key, value) in self
let keyString = "\(key)".urlEncoded
let valueString = "\(value)".urlEncoded
let query = "\(keyString)=\(valueString)"
parts.append(query)
return parts.joined(separator: "&")
extension Date
func toMillis() -> Int64!
return Int64(self.timeIntervalSince1970 * 1000)
extension StringProtocol
var containsLetters: Bool contains $0.isLetter
public extension CharacterSet
static let urlQueryParameterAllowed = CharacterSet.urlQueryAllowed.subtracting(CharacterSet(charactersIn: "&?"))
static let urlQueryDenied = CharacterSet.urlQueryAllowed.inverted()
static let urlQueryKeyValueDenied = CharacterSet.urlQueryParameterAllowed.inverted()
static let urlPathDenied = CharacterSet.urlPathAllowed.inverted()
static let urlFragmentDenied = CharacterSet.urlFragmentAllowed.inverted()
static let urlHostDenied = CharacterSet.urlHostAllowed.inverted()
static let urlDenied = CharacterSet.urlQueryDenied
.union(.urlQueryKeyValueDenied)
.union(.urlPathDenied)
.union(.urlFragmentDenied)
.union(.urlHostDenied)
func inverted() -> CharacterSet
var copy = self
copy.invert()
return copy
public extension String
func urlEncoded(denying deniedCharacters: CharacterSet = .urlDenied) -> String?
return addingPercentEncoding(withAllowedCharacters: deniedCharacters.inverted())
【问题讨论】:
查看其他语言的关于 OAuth 和 NetSuite 的 SO 问题。前任。德尔福:***.com/questions/57957730Java:***.com/questions/57957730php:***.com/questions/32867476 请检查代码。let base_url = "baseurl"
需要真正的基本网址(不带参数)。
基本 URL 已更新。在这里更改以发布。但在 xcode 中是正确的。仍然得到与 "error" : "code" : "INVALID_LOGIN_ATTEMPT", "message" : "Invalid login attempt." 相同的响应。
您是否在 Postman 或类似工具中进行了测试调用,以确保它与签名相关而不是权限问题?
【参考方案1】:
目前看到一个已确认的错误。
var TIME_STAMP = String(Date().toMillis())
时间戳必须是自 1970 年以来的秒数,而不是毫秒。您引用的 Java 代码检索毫秒并将其除以 1000 得到秒。
String TIME_STAMP = String.valueOf(System.currentTimeMillis() / 1000);
您没有从下面代码中的 baseString 和 Key 生成 HMAC。
func computeSignature(baseString: String, keyString: String) -> String
let hmacResult:String = keyString.hmac(algorithm: HMACAlgorithm.SHA1, key: keyString)
return hmacResult
【讨论】:
即使在几秒钟内发送时间戳后,它仍然会出现同样的错误。 您使用了太多自己的代码 - 应该有易于访问和测试的代码来对字符串进行 URI 编码、生成 HMAC 等。以上是关于来自 swift iOS 的 Netsuite Oauth 1.0 身份验证的主要内容,如果未能解决你的问题,请参考以下文章
来自对象的 JSON 在 iOS Swift 3 中出现数组问题