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.. 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() } }