Ios 刷新令牌的正确方法

Ios 刷新令牌的正确方法,ios,swift,alamofire,Ios,Swift,Alamofire,RequestManager类中有一个函数getUser,它在myVC中调用 func getUser(onCompletion: @escaping (_ result: User?, error: String?) -> Void) { Alamofire.request(Router.getUser).responseJSON { (response) in // here is the work with response } } 如果此请求返回4

RequestManager类中有一个函数
getUser
,它在my
VC
中调用

func getUser(onCompletion: @escaping (_ result: User?, error: String?) -> Void) {
    Alamofire.request(Router.getUser).responseJSON { (response) in
        // here is the work with response
    }
}
如果此请求返回
403
,则表示
access\u令牌
已过期。我需要刷新令牌并从我的
VC
重复请求

func getUser(onCompletion: @escaping (_ result: User?, error: String?) -> Void) {
    Alamofire.request(Router.getUser).responseJSON { (response) in
        // here is the work with response
    }
}
现在是问题

如何刷新令牌并以正确的方式重复请求

MyViewController
getUser
方法中处理错误和刷新令牌不是个好主意,因为我有很多
VCs
请求方法

我需要这样的东西:
VC
调用该方法并获取
用户
,即使令牌已过期,
refreshtToken
必须在所有请求方法中

编辑

refreshToken
方法

func refreshToken(onCompletion: @escaping (_ result: Bool?) -> Void) {
    Alamofire.request(Router.refreshToken).responseJSON { (response) in
        print(response)
        if response.response?.statusCode == 200 {
            guard let data = response.data else { return onCompletion(false) }
            let token = try? JSONDecoder().decode(Token.self, from: data)
            token?.setToken()
            onCompletion(true)
        } else {
            onCompletion(false)
        }
    }
}

为了解决这个问题,我创建了一个类,我们将从中调用每个API,比如
BaseService.swift

BaseService.swift:

import Foundation
import Alamofire
import iComponents

struct AlamofireRequestModal {
    var method: Alamofire.HTTPMethod
    var path: String
    var parameters: [String: AnyObject]?
    var encoding: ParameterEncoding
    var headers: [String: String]?

    init() {
        method = .get
        path = ""
        parameters = nil
        encoding = JSONEncoding() as ParameterEncoding
        headers = ["Content-Type": "application/json",
                   "X-Requested-With": "XMLHttpRequest",
                   "Cache-Control": "no-cache"]
    }
}

class BaseService: NSObject {

    func callWebServiceAlamofire(_ alamoReq: AlamofireRequestModal, success: @escaping ((_ responseObject: AnyObject?) -> Void), failure: @escaping ((_ error: NSError?) -> Void)) {

        // Create alamofire request
        // "alamoReq" is overridden in services, which will create a request here
        let req = Alamofire.request(alamoReq.path, method: alamoReq.method, parameters: alamoReq.parameters, encoding: alamoReq.encoding, headers: alamoReq.headers)

        // Call response handler method of alamofire
        req.validate(statusCode: 200..<600).responseJSON(completionHandler: { response in
            let statusCode = response.response?.statusCode

            switch response.result {
            case .success(let data):

                if statusCode == 200 {
                    Logs.DLog(object: "\n Success: \(response)")
                    success(data as AnyObject?)

                } else if statusCode == 403 {
                    // Access token expire
                    self.requestForGetNewAccessToken(alaomReq: alamoReq, success: success, failure: failure)

                } else {
                    let errorDict: [String: Any] = ((data as? NSDictionary)! as? [String: Any])!
                    Logs.DLog(object: "\n \(errorDict)")
                    failure(errorTemp as NSError?)
                }
            case .failure(let error):
                Logs.DLog(object: "\n Failure: \(error.localizedDescription)")
                failure(error as NSError?)
            }
        })
    }

}

extension BaseService {

    func getAccessToken() -> String {
        if let accessToken =  UserDefaults.standard.value(forKey: UserDefault.userAccessToken) as? String {
            return "Bearer " + accessToken
        } else {
            return ""
        }
    }

    // MARK: - API CALL
    func requestForGetNewAccessToken(alaomReq: AlamofireRequestModal, success: @escaping ((_ responseObject: AnyObject?) -> Void), failure: @escaping ((_ error: NSError?) -> Void) ) {

        UserModal().getAccessToken(success: { (responseObj) in
            if let accessToken = responseObj?.value(forKey: "accessToken") {
                UserDefaults.standard.set(accessToken, forKey: UserDefault.userAccessToken)
            }

            // override existing alaomReq (updating token in header)
            var request: AlamofireRequestModal = alaomReq
            request.headers = ["Content-Type": "application/json",
                               "X-Requested-With": "XMLHttpRequest",
                               "Cache-Control": "no-cache",
                               "X-Authorization": self.getAccessToken()]

            self.callWebServiceAlamofire(request, success: success, failure: failure)

        }, failure: { (_) in
            self.requestForGetNewAccessToken(alaomReq: alaomReq, success: success, failure: failure)
        })
    }

}
import Foundation

let GET_USER_PROFILE_METHOD = "user/profile"

struct BaseURL {
    // Local Server
    static let urlString: String = "http://192.168.10.236: 8084/"
    // QAT Server
    // static let urlString: String = "http://192.171.286.74: 8080/"

    static let staging: String = BaseURL.urlString + "api/v1/"
}

class APIService: BaseService {

    func getUserProfile(success: @escaping ((_ responseObject: AnyObject?) -> Void), failure: @escaping ((_ error: NSError?) -> Void)) {

        var request: AlamofireRequestModal = AlamofireRequestModal()
        request.method = .get
        request.path = BaseURL.staging + GET_USER_PROFILE_METHOD
        request.headers = ["Content-Type": "application/json",
                           "X-Requested-With": "XMLHttpRequest",
                           "Cache-Control": "no-cache",
                           "X-Authorization": getAccessToken()]

        self.callWebServiceAlamofire(request, success: success, failure: failure)
    }

}
说明:

import Foundation
import Alamofire
import iComponents

struct AlamofireRequestModal {
    var method: Alamofire.HTTPMethod
    var path: String
    var parameters: [String: AnyObject]?
    var encoding: ParameterEncoding
    var headers: [String: String]?

    init() {
        method = .get
        path = ""
        parameters = nil
        encoding = JSONEncoding() as ParameterEncoding
        headers = ["Content-Type": "application/json",
                   "X-Requested-With": "XMLHttpRequest",
                   "Cache-Control": "no-cache"]
    }
}

class BaseService: NSObject {

    func callWebServiceAlamofire(_ alamoReq: AlamofireRequestModal, success: @escaping ((_ responseObject: AnyObject?) -> Void), failure: @escaping ((_ error: NSError?) -> Void)) {

        // Create alamofire request
        // "alamoReq" is overridden in services, which will create a request here
        let req = Alamofire.request(alamoReq.path, method: alamoReq.method, parameters: alamoReq.parameters, encoding: alamoReq.encoding, headers: alamoReq.headers)

        // Call response handler method of alamofire
        req.validate(statusCode: 200..<600).responseJSON(completionHandler: { response in
            let statusCode = response.response?.statusCode

            switch response.result {
            case .success(let data):

                if statusCode == 200 {
                    Logs.DLog(object: "\n Success: \(response)")
                    success(data as AnyObject?)

                } else if statusCode == 403 {
                    // Access token expire
                    self.requestForGetNewAccessToken(alaomReq: alamoReq, success: success, failure: failure)

                } else {
                    let errorDict: [String: Any] = ((data as? NSDictionary)! as? [String: Any])!
                    Logs.DLog(object: "\n \(errorDict)")
                    failure(errorTemp as NSError?)
                }
            case .failure(let error):
                Logs.DLog(object: "\n Failure: \(error.localizedDescription)")
                failure(error as NSError?)
            }
        })
    }

}

extension BaseService {

    func getAccessToken() -> String {
        if let accessToken =  UserDefaults.standard.value(forKey: UserDefault.userAccessToken) as? String {
            return "Bearer " + accessToken
        } else {
            return ""
        }
    }

    // MARK: - API CALL
    func requestForGetNewAccessToken(alaomReq: AlamofireRequestModal, success: @escaping ((_ responseObject: AnyObject?) -> Void), failure: @escaping ((_ error: NSError?) -> Void) ) {

        UserModal().getAccessToken(success: { (responseObj) in
            if let accessToken = responseObj?.value(forKey: "accessToken") {
                UserDefaults.standard.set(accessToken, forKey: UserDefault.userAccessToken)
            }

            // override existing alaomReq (updating token in header)
            var request: AlamofireRequestModal = alaomReq
            request.headers = ["Content-Type": "application/json",
                               "X-Requested-With": "XMLHttpRequest",
                               "Cache-Control": "no-cache",
                               "X-Authorization": self.getAccessToken()]

            self.callWebServiceAlamofire(request, success: success, failure: failure)

        }, failure: { (_) in
            self.requestForGetNewAccessToken(alaomReq: alaomReq, success: success, failure: failure)
        })
    }

}
import Foundation

let GET_USER_PROFILE_METHOD = "user/profile"

struct BaseURL {
    // Local Server
    static let urlString: String = "http://192.168.10.236: 8084/"
    // QAT Server
    // static let urlString: String = "http://192.171.286.74: 8080/"

    static let staging: String = BaseURL.urlString + "api/v1/"
}

class APIService: BaseService {

    func getUserProfile(success: @escaping ((_ responseObject: AnyObject?) -> Void), failure: @escaping ((_ error: NSError?) -> Void)) {

        var request: AlamofireRequestModal = AlamofireRequestModal()
        request.method = .get
        request.path = BaseURL.staging + GET_USER_PROFILE_METHOD
        request.headers = ["Content-Type": "application/json",
                           "X-Requested-With": "XMLHttpRequest",
                           "Cache-Control": "no-cache",
                           "X-Authorization": getAccessToken()]

        self.callWebServiceAlamofire(request, success: success, failure: failure)
    }

}
在代码块中:

else if statusCode == 403 {
    // Access token expire
    self.requestForGetNewAccessToken(alaomReq: alamoReq, success: success, failure: failure)
}
我使用请求调用getNewAccessTokenAPI(在您的例子中是刷新令牌)(它可以是基于APIService.swift的任何请求)


当我们获得新令牌时,我保存它用户默认值,然后我将更新请求(我在刷新令牌API调用中作为参数获得的请求),并将按原样传递成功和失败块。

您可以创建通用刷新类:

protocol IRefresher {
    associatedtype RefreshTarget: IRefreshing

    var target: RefreshTarget? { get }

    func launch(repeats: Bool, timeInterval: TimeInterval)
    func invalidate()
}

class Refresher<T: IRefreshing>: IRefresher {

    internal weak var target: T?
    private var timer: Timer?

    init(target: T?) {
        self.target = target
    }

    public func launch(repeats: Bool, timeInterval: TimeInterval) {
        timer = Timer.scheduledTimer(withTimeInterval: timeInterval, repeats: repeats) { [weak self] (timer) in
            self?.target?.refresh()
        }
    }

    public func invalidate() {
        timer?.invalidate()
    }
}
定义新的typealias:

typealias RequestManagerRefresher = Refresher<RequestManager>
并展开RequestManager:

extension RequestManager: IRefreshing {
    func refresh() {
        updateToken()
    }
}
class RequestManager {
    static var updateInterval: TimeInterval = 0

    let refresher: RequestManagerRefresher

    init() {
        refresher = Refresher(target: self)
        refresher?.launch(repeats: true, timeInterval: updateInterval)
    }
}
每15分钟将更新RequestManager的令牌


更新 当然,您也可以更改更新时间。创建一个存储所需更新时间的静态变量。例如,在RequestManager中:

extension RequestManager: IRefreshing {
    func refresh() {
        updateToken()
    }
}
class RequestManager {
    static var updateInterval: TimeInterval = 0

    let refresher: RequestManagerRefresher

    init() {
        refresher = Refresher(target: self)
        refresher?.launch(repeats: true, timeInterval: updateInterval)
    }
}
因此,现在您可以向令牌提供程序服务器询问令牌更新间隔,并将此值设置为updateInterval静态变量:

backendTokenUpdateIntervalRequest() { interval in
    RequestManager.updateInterval = interval
}

将刷新令牌API调用的代码显示为well@AnkitJayaswal有必要吗?have addedI不需要每15分钟更新一次我的令牌,但只有在令牌过期时才需要更新@vboyko@aaisataev更新后,您应该询问后端服务器的过期时间间隔value@vboyko请您解释一下这是从哪里来的“authenticationService.refresher=refresher(target:self)”。“我很难弄明白这一点。”法泽拉罕默德说。这一行以自我为目标创建刷新器,并将其设置为属性。好极了,兄弟。正是我想要的东西。谢谢:-)