99热99这里只有精品6国产,亚洲中文字幕在线天天更新,在线观看亚洲精品国产福利片 ,久久久久综合网

歡迎加入QQ討論群258996829
我心依舊 頭像
蘋果1袋
1
我心依舊

Alamofire隔離網(wǎng)絡(luò)框架封裝 + 簽名 + ssl證書信任

發(fā)布時(shí)間:2016-12-06 16:54  回復(fù):0  查看:4035   最后回復(fù):2016-12-06 16:54  

Apple在元旦開始就要強(qiáng)制iOS開發(fā)者們使用 https 請(qǐng)求了,就來一篇吧。簡單的講,強(qiáng)制iOS開發(fā)者使用https的作用就是為了防止應(yīng)用在和后端進(jìn)行數(shù)據(jù)通信過程中傳輸?shù)臄?shù)據(jù)被第三方中間攻擊(篡改請(qǐng)求),或者被抓包工具獲取我們傳輸?shù)臄?shù)據(jù),從而提升我們數(shù)據(jù)的安全性。https參考鏈接:http://baike.baidu.com/view/14121.htm 。

封裝請(qǐng)求工具類

我們?cè)谑褂玫谌骄W(wǎng)絡(luò)請(qǐng)求庫時(shí),一般都不會(huì)直接調(diào)用第三方庫的方法,而是自己基于第三方庫封裝一個(gè)工具類,并提供一個(gè)單利對(duì)象用于網(wǎng)絡(luò)請(qǐng)求。從而降低耦合度和代碼污染,也方便統(tǒng)一處于網(wǎng)絡(luò)請(qǐng)求和后期替換網(wǎng)絡(luò)框架等。

我這里這個(gè)例子比較簡單,僅作為本文章演示簽名和ssl證書信任的demo代碼,實(shí)際項(xiàng)目請(qǐng)根據(jù)實(shí)際需求去封裝即可。

自定義響應(yīng)枚舉

/// 請(qǐng)求響應(yīng)狀態(tài)
///
/// - success: 響應(yīng)成功  - 就是成功
/// - unusual: 響應(yīng)異常  - 例如 手機(jī)已被注冊(cè)
/// - notLogin: 未登錄   - 例如 token到期
/// - failure: 請(qǐng)求錯(cuò)誤  - 例如 比如網(wǎng)絡(luò)錯(cuò)誤
enum JFResponseStatus: Int {
    case success  = 0
    case unusual  = 1
    case notLogin = 2
    case failure  = 3
}

用來統(tǒng)一處理請(qǐng)求可能導(dǎo)致的結(jié)果,具體按照項(xiàng)目需求來定義,如果項(xiàng)目需要,還可以定義一套客戶端的錯(cuò)誤碼表,以便我們更好的定位錯(cuò)誤。

請(qǐng)求回調(diào)閉包

/// 網(wǎng)絡(luò)請(qǐng)求回調(diào)
typealias NetworkFinished = (_ status: JFResponseStatus, _ result: JSON?, _ tipString: String?) -> ()

將響應(yīng)狀態(tài)枚舉、后端返回的數(shù)據(jù)和提示語回調(diào)給調(diào)用者,具體也需要根據(jù)自己項(xiàng)目的實(shí)際情況來定義。

工具類單利對(duì)象

/// 網(wǎng)絡(luò)工具類單例
static let shareNetworkTool = JFNetworkTools()
請(qǐng)求網(wǎng)絡(luò)我們不需要重復(fù)創(chuàng)建很多對(duì)象,所以只需要使用單利即可,節(jié)約內(nèi)存也方便調(diào)用。

配置SessionManager

并且我們?cè)诘谝淮潍@取單利對(duì)象的同時(shí),需要配置好ssl證書信任策略。首先將后臺(tái)給我們的證書文件導(dǎo)入項(xiàng)目中,我這里命名為 cert.cer 。然后配置 Alamofire 提供的 SessionManager 對(duì)象,這個(gè)對(duì)象以后將作為我們請(qǐng)求接口使用。

fileprivate var afManager: SessionManager!
 
override init() {
    super.init()
    
    // 確保項(xiàng)目已經(jīng)成功導(dǎo)入了cert.cer證書文件
    let pathToCert = Bundle.main.path(forResource: "cert", ofType: "cer")
    let localCertificate = NSData(contentsOfFile: pathToCert!)
    let certificates = [SecCertificateCreateWithData(nil, localCertificate!)!]
    
    let serverTrustPolicy = ServerTrustPolicy.pinCertificates(
        certificates: certificates,
        validateCertificateChain: true,
        validateHost: true
    )
    let serverTrustPolicies = ["不帶協(xié)議頭的域名" : serverTrustPolicy]
    let serverTrustPolicyManager = ServerTrustPolicyManager(policies: serverTrustPolicies)
    
    afManager = SessionManager(
        configuration: URLSessionConfiguration.default,
        serverTrustPolicyManager: serverTrustPolicyManager
    )
    
}

設(shè)置公共請(qǐng)求頭

我們一般會(huì)將app的一些公共版本信息、token/session、簽名等設(shè)置為請(qǐng)求頭,這里我們主要說的是簽名的問題。為了防止API調(diào)用過程中被黑客惡意篡改,我們調(diào)用API的時(shí)候都會(huì)對(duì)請(qǐng)求進(jìn)行簽名,后端也會(huì)對(duì)簽名進(jìn)行驗(yàn)證,如果驗(yàn)證不通過,請(qǐng)求也就不會(huì)繼續(xù)處理了。(我們和后端使用相同的簽名算法,并且比較最終簽名結(jié)果,從而確保請(qǐng)求未被篡改)

參考地址:http://open.taobao.com/docs/doc.htm?spm=a3142.7395905.4.16.eQ4qIu&articleId=101617&docType=1&treeId=1 。

具體算法以后端提供的為準(zhǔn),我們只需根據(jù)后端提供的算法進(jìn)行處理即可。這里我采用較為通用的算法:hex(md5(headers + parameters + secret)) ,其中 secret 是和后端協(xié)商的API密鑰。

/// 獲取請(qǐng)求頭
///
/// - Returns: 請(qǐng)求頭
fileprivate func getHTTPHeaders(parameters: [String : Any]?) -> [String : String] {
    
    // api版本
    var headers = [
        "X-API-VERSION" : API_VERSION,
        ]
    
    // token
    if let token = JFUserModel.shareAccount()?.token {
        headers["Authorization"] = "Bearer \(token)"
        print("請(qǐng)求頭加入了token", "Bearer \(token)")
    }
    
    // uuid
    if let uuid = JFObjcTools.uuidString() {
        headers["X-DEVICE-ID"] = uuid
        print("請(qǐng)求頭加入了device", uuid)
    }
    
    // 請(qǐng)求簽名
    // 字典key排序
    var keys = [String]()
    if let parameters = parameters {
        for key in parameters.keys {
            keys.append(key)
        }
    }
    for key in headers.keys {
        keys.append(key)
    }
    keys = keys.sorted(by: {$0 < $1})
    
    // 拼接排序后的參數(shù)
    var jointParameters = ""
    for key in keys {
        if headers.keys.contains(key) {
            jointParameters += key
            jointParameters += "\(headers[key]!)"
        }
        if let parameters = parameters {
            if parameters.keys.contains(key) {
                jointParameters += key
                jointParameters += "\(parameters[key]!)"
            }
        }
        
    }
    jointParameters += API_SECRET
    
    let md5Hex =  jointParameters.md5Data()!.map { String(format: "%02hhx", $0) }.joined().uppercased()
    
    // 算法 hex(md5(headers + parameters + secret))
    headers["X-SIGNATURE"] = md5Hex
    print("請(qǐng)求頭加入了signature", md5Hex)
    
    return headers
}

封裝基本請(qǐng)求方法

接下來我們就可以封裝我們的基本請(qǐng)求方法了,這里我封裝了基本的 get/post 請(qǐng)求,并使用我們前面配置好的 SessionManager 對(duì)象來處理網(wǎng)絡(luò)請(qǐng)求。這樣我們的請(qǐng)求就不會(huì)被 Charles 等抓包工具抓取數(shù)據(jù)或者請(qǐng)求被惡意篡改了。

/// GET請(qǐng)求
///
/// - Parameters:
///   - APIString: urlString
///   - parameters: 參數(shù)
///   - finished: 完成回調(diào)
func get(APIString: String, parameters: [String : Any]?, finished: @escaping NetworkFinished) {
    
    UIApplication.shared.isNetworkActivityIndicatorVisible = true
    afManager.request(APIString, parameters: parameters, headers: getHTTPHeaders(parameters: parameters)).responseJSON { (response) in
        self.handle(response: response, finished: finished)
    }
}
 
/// POST請(qǐng)求
///
/// - Parameters:
///   - APIString: urlString
///   - parameters: 參數(shù)
///   - finished: 完成回調(diào)
func post(APIString: String, parameters: [String : Any]?, finished: @escaping NetworkFinished) {
    
    UIApplication.shared.isNetworkActivityIndicatorVisible = true
    afManager.request(APIString, method: .post,parameters: parameters, headers: getHTTPHeaders(parameters: parameters)).responseJSON { (response) in
        self.handle(response: response, finished: finished)
    }
}
 
/// 處理響應(yīng)結(jié)果
///
/// - Parameters:
///   - response: 響應(yīng)對(duì)象
///   - finished: 完成回調(diào)
fileprivate func handle(response: DataResponse<Any>, finished: @escaping NetworkFinished) {
    UIApplication.shared.isNetworkActivityIndicatorVisible = false
    
    switch response.result {
    case .success(let value):
        
        print(response.request?.url ?? "", value)
        let json = JSON(value)
        if json["code"].intValue == 0 {
            finished(.success, json["data"], nil)
        } else if json["code"].intValue == 20104 {
            JFProgressHUD.dismiss()
            // 注銷登錄
            JFUserModel.logout()
            finished(.notLogin, nil, "請(qǐng)重新登錄(\(json["code"].intValue))")
        } else {
            finished(.unusual, nil, json["message"].stringValue + "(\(json["code"].intValue))")
        }
        
    case .failure(let error):
        finished(.failure, nil, error.localizedDescription)
    }
}

封裝網(wǎng)絡(luò)工具類并沒有完成,這里只是一個(gè)演示demo而已,具體大家自己根據(jù)自己的業(yè)務(wù)邏輯和后臺(tái)響應(yīng)來封裝吧。

完整代碼

JFNetworkTools.swift

import UIKit
import Alamofire
import SwiftyJSON
 
/// 請(qǐng)求響應(yīng)狀態(tài)
///
/// - success: 響應(yīng)成功  - 就是成功
/// - unusual: 響應(yīng)異常  - 例如 手機(jī)已被注冊(cè)
/// - notLogin: 未登錄   - 例如 token到期
/// - failure: 請(qǐng)求錯(cuò)誤  - 例如 比如網(wǎng)絡(luò)錯(cuò)誤
enum JFResponseStatus: Int {
    case success  = 0
    case unusual  = 1
    case notLogin = 2
    case failure  = 3
}
 
/// 網(wǎng)絡(luò)請(qǐng)求回調(diào)
typealias NetworkFinished = (_ status: JFResponseStatus, _ result: JSON?, _ tipString: String?) -> ()
 
class JFNetworkTools: NSObject {
    
    /// 網(wǎng)絡(luò)工具類單例
    static let shareNetworkTool = JFNetworkTools()
    
    fileprivate var afManager: SessionManager!
 
    override init() {
        super.init()
        
        print("配置ssl,防止抓包工具抓取數(shù)據(jù)")
        let pathToCert = Bundle.main.path(forResource: "cert", ofType: "cer")
        let localCertificate = NSData(contentsOfFile: pathToCert!)
        let certificates = [SecCertificateCreateWithData(nil, localCertificate!)!]
        
        let serverTrustPolicy = ServerTrustPolicy.pinCertificates(
            certificates: certificates,
            validateCertificateChain: true,
            validateHost: true
        )
        let serverTrustPolicies = ["xxxxxxxx" : serverTrustPolicy]
        let serverTrustPolicyManager = ServerTrustPolicyManager(policies: serverTrustPolicies)
        
        afManager = SessionManager(
            configuration: URLSessionConfiguration.default,
            serverTrustPolicyManager: serverTrustPolicyManager
        )
        
    }
    
    /// 獲取請(qǐng)求頭
    ///
    /// - Returns: 請(qǐng)求頭
    fileprivate func getHTTPHeaders(parameters: [String : Any]?) -> [String : String] {
        
        // api版本
        var headers = [
            "X-API-VERSION" : API_VERSION,
            ]
        
        // token
        if let token = JFUserModel.shareAccount()?.token {
            headers["Authorization"] = "Bearer \(token)"
            print("請(qǐng)求頭加入了token", "Bearer \(token)")
        }
        
        // uuid
        if let uuid = JFObjcTools.uuidString() {
            headers["X-DEVICE-ID"] = uuid
            print("請(qǐng)求頭加入了device", uuid)
        }
        
        // 請(qǐng)求簽名
        // 字典key排序
        var keys = [String]()
        if let parameters = parameters {
            for key in parameters.keys {
                keys.append(key)
            }
        }
        for key in headers.keys {
            keys.append(key)
        }
        keys = keys.sorted(by: {$0 < $1})
        
        // 拼接排序后的參數(shù)
        var jointParameters = ""
        for key in keys {
            if headers.keys.contains(key) {
                jointParameters += key
                jointParameters += "\(headers[key]!)"
            }
            if let parameters = parameters {
                if parameters.keys.contains(key) {
                    jointParameters += key
                    jointParameters += "\(parameters[key]!)"
                }
            }
            
        }
        jointParameters += API_SECRET
        
        let md5Hex =  jointParameters.md5Data()!.map { String(format: "%02hhx", $0) }.joined().uppercased()
        
        // 算法 hex(md5(headers + parameters + secret))
        headers["X-SIGNATURE"] = md5Hex
        print("請(qǐng)求頭加入了signature", md5Hex)
        
        return headers
    }
 
}
 
// MARK: - 普通get/post請(qǐng)求方法,可以根據(jù)自己的業(yè)務(wù)多封裝一些通用請(qǐng)求方法
extension JFNetworkTools {
    
    /**
     GET請(qǐng)求
     
     - parameter APIString:  urlString
     - parameter parameters: 參數(shù)
     - parameter finished:   完成回調(diào)
     */
    func get(APIString: String, parameters: [String : Any]?, finished: @escaping NetworkFinished) {
        
        UIApplication.shared.isNetworkActivityIndicatorVisible = true
        afManager.request(APIString, parameters: parameters, headers: getHTTPHeaders(parameters: parameters)).responseJSON { (response) in
            self.handle(response: response, finished: finished)
        }
    }
 
    /**
     POST請(qǐng)求
     
     - parameter APIString:  urlString
     - parameter parameters: 參數(shù)
     - parameter finished:   完成回調(diào)
     */
    func post(APIString: String, parameters: [String : Any]?, finished: @escaping NetworkFinished) {
        
        UIApplication.shared.isNetworkActivityIndicatorVisible = true
        afManager.request(APIString, method: .post,parameters: parameters, headers: getHTTPHeaders(parameters: parameters)).responseJSON { (response) in
            self.handle(response: response, finished: finished)
        }
    }
 
    /// 處理響應(yīng)結(jié)果
    ///
    /// - Parameters:
    ///   - response: 響應(yīng)對(duì)象
    ///   - finished: 完成回調(diào)
    fileprivate func handle(response: DataResponse<Any>, finished: @escaping NetworkFinished) {
        UIApplication.shared.isNetworkActivityIndicatorVisible = false
        
        switch response.result {
        case .success(let value):
            
            print(response.request?.url ?? "", value)
            let json = JSON(value)
            if json["code"].intValue == 0 {
                finished(.success, json["data"], nil)
            } else if json["code"].intValue == 20104 {
                JFProgressHUD.dismiss()
                // 注銷登錄
                JFUserModel.logout()
                finished(.notLogin, nil, "請(qǐng)重新登錄(\(json["code"].intValue))")
            } else {
                finished(.unusual, nil, json["message"].stringValue + "(\(json["code"].intValue))")
            }
            
        case .failure(let error):
            finished(.failure, nil, error.localizedDescription)
        }
    }
    
}
 
// MARK: - 簡單json緩存 本來不應(yīng)該放這里的,如果項(xiàng)目需要緩存,應(yīng)該有自己的DAL(Data access layer)工具類。參考地址:https://blog.6ag.cn/1551.html
extension JFNetworkTools {
    
    /**
     緩存json數(shù)據(jù)為指定json文件
     
     - parameter json:     JSON對(duì)象
     - parameter jsonPath: json文件路徑
     */
    func saveJson(_ json: JSON, jsonPath: String) {
        do {
            if let json = json.rawString() {
                try json.write(toFile: jsonPath, atomically: true, encoding: String.Encoding.utf8)
                print("緩存數(shù)據(jù)成功", jsonPath)
            }
        } catch {
            print("緩存數(shù)據(jù)失敗", jsonPath)
        }
    }
    
    /**
     刪除指定文件
     
     - parameter jsonPath: 要?jiǎng)h除的json文件路徑
     */
    func removeJson(_ jsonPath: String) {
        let fileManager = FileManager.default
        if fileManager.fileExists(atPath: jsonPath) {
            do {
                try fileManager.removeItem(atPath: jsonPath)
                print("刪除成功", jsonPath)
            } catch {
                print("刪除失敗", jsonPath)
            }
        }
    }
    
    /**
     獲取緩存的json數(shù)據(jù)
     
     - parameter jsonPath: json文件路徑
     
     - returns: JSON對(duì)象
     */
    func getJson(_ jsonPath: String) -> JSON? {
        if let data = try? Data(contentsOf: URL(fileURLWithPath: jsonPath)) {
            print("獲取緩存數(shù)據(jù)成功", jsonPath)
            let json = JSON(data: data)
            return json
        }
        print("獲取緩存數(shù)據(jù)失敗", jsonPath)
        return nil
    }
}
 
// MARK: - 公共業(yè)務(wù)封裝
extension JFNetworkTools {
    
    /// 檢測(cè)是否能夠簽到
    func canCheckin(finished: @escaping (_ can: Bool) -> ()) {
        
        JFNetworkTools.shareNetworkTool.get(APIString: API_CHECKIN, parameters: nil) { (status, result, tipString) in
            switch status {
            case .success:
                isCanCheckin = result!["can_checkin"].boolValue
                finished(isCanCheckin)
            case .unusual:
                finished(false)
            case .notLogin:
                finished(false)
            case .failure:
                finished(false)
            }
        }
        
    }
 
    // 可以封裝一些公用的業(yè)務(wù)接口......... 
}
 
// MARK: - 網(wǎng)絡(luò)工具方法
extension JFNetworkTools {
    
    /**
     獲取當(dāng)前網(wǎng)絡(luò)狀態(tài)
     
     - returns: 0未知 1WiFi 2WAN
     */
    func getCurrentNetworkState() -> Int {
        return Reachability.forInternetConnection().currentReachabilityStatus().rawValue
    }
 
    // 可以封裝一些網(wǎng)絡(luò)相關(guān)的工具方法......
}

使用工具類請(qǐng)求api接口

JFNetworkTools.shareNetworkTool.get(APIString: API_GAME_LIST, parameters: parameters) { (status, result, tipString) in
    switch status {
    case .success:
        break
    case .unusual:
        break
    case .notLogin:
        break
    case .failure:
        break
    }
}
如有不對(duì)的地方,歡迎大神指正。

來源:Alamofire隔離網(wǎng)絡(luò)框架封裝 + 簽名 + ssl證書信任 | 六阿哥博客https://blog.6ag.cn/1723.html

您還未登錄,請(qǐng)先登錄

熱門帖子

最新帖子

?