83 lines
2.7 KiB
Swift
83 lines
2.7 KiB
Swift
import Foundation
|
||
import Moya
|
||
import CommonCrypto
|
||
|
||
/// JiaMiKey 的 MD5,预计算避免每次请求都算
|
||
private let jiaMiKeyMD5: String = {
|
||
let key = "857d69d374694c1de46486d3bdaaac43"
|
||
return SignPlugin.md5(key)
|
||
}()
|
||
|
||
/// 请求签名 Plugin
|
||
final class SignPlugin: PluginType {
|
||
|
||
func prepare(_ request: URLRequest, target: any TargetType) -> URLRequest {
|
||
var mutableRequest = request
|
||
|
||
guard let url = request.url else { return mutableRequest }
|
||
|
||
let timestamp = String(format: "%.0lf", Date().timeIntervalSince1970)
|
||
let nonce = randomString(length: 32)
|
||
|
||
var queryDict: [String: String] = [
|
||
"timestamp": timestamp,
|
||
"nonce": nonce
|
||
]
|
||
|
||
// 收集 URL 已有的 query 参数,一并纳入签名
|
||
if let components = URLComponents(url: url, resolvingAgainstBaseURL: false),
|
||
let queryItems = components.queryItems {
|
||
for item in queryItems {
|
||
queryDict[item.name] = item.value ?? ""
|
||
}
|
||
}
|
||
|
||
let bodyString: String
|
||
if let httpBody = request.httpBody, !httpBody.isEmpty {
|
||
bodyString = String(data: httpBody, encoding: .utf8) ?? ""
|
||
} else {
|
||
bodyString = ""
|
||
}
|
||
|
||
let queryString = sortedQueryString(queryDict)
|
||
let signStr: String
|
||
if bodyString.isEmpty {
|
||
signStr = queryString + "&" + jiaMiKeyMD5
|
||
} else {
|
||
signStr = queryString + "&" + bodyString + "&" + jiaMiKeyMD5
|
||
}
|
||
let signature = Self.md5(signStr)
|
||
|
||
// 剥离旧 query,替换为完整的签名 query
|
||
let signedQuery = queryString + "&signature=" + signature
|
||
var components = URLComponents(url: url, resolvingAgainstBaseURL: false)
|
||
components?.percentEncodedQuery = signedQuery.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)
|
||
|
||
if let newURL = components?.url {
|
||
mutableRequest.url = newURL
|
||
}
|
||
|
||
return mutableRequest
|
||
}
|
||
|
||
private func sortedQueryString(_ dict: [String: String]) -> String {
|
||
dict
|
||
.sorted { $0.key < $1.key }
|
||
.map { "\($0.key)=\($0.value)" }
|
||
.joined(separator: "&")
|
||
}
|
||
|
||
private func randomString(length: Int) -> String {
|
||
let chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
||
return String((0..<length).map { _ in chars.randomElement()! })
|
||
}
|
||
|
||
fileprivate static func md5(_ string: String) -> String {
|
||
let cStr = string.cString(using: .utf8)
|
||
let length = CC_LONG(strlen(cStr!))
|
||
var digest = [UInt8](repeating: 0, count: Int(CC_MD5_DIGEST_LENGTH))
|
||
CC_MD5(cStr, length, &digest)
|
||
return digest.map { String(format: "%02x", $0) }.joined()
|
||
}
|
||
}
|