관리 메뉴

김종권의 iOS 앱 개발 알아가기

[iOS - swift] 3. Alamofire 사용 방법 - 토큰 갱신 (AuthenticationCredential, Authenticator, AuthenticationInterceptor) 본문

iOS 응용 (swift)

[iOS - swift] 3. Alamofire 사용 방법 - 토큰 갱신 (AuthenticationCredential, Authenticator, AuthenticationInterceptor)

jake-kim 2021. 10. 16. 15:33

1. Alamofire 사용 방법 - Network Layer 구현 (Moya 프레임워크처럼 사용하는 방법)

2. Alamofire 사용 방법 - 토큰 갱신 방법1 (Interceptor, adapt, retry)

3. Alamofire 사용 방법 - 토큰 갱신 (AuthenticationCredential, Authenticator, AuthenticationInterceptor)

4. Alamofire 사용 방법 - 로그 Log (EventMonitor)


AuthenticationCredential, Authenticator, AuthenticationInterceptor,

  • 기존에는 RequestInterceptor를 준수하여 adapt, retry를 구현하여 사용했지만 Alamofire 5.2부터 새로 인증 전용 프로토콜이 추가되어 사용
  • AuthenticationCredential와 Authenticator 프로토콜을 준수하는 구현체를 만들고 그 두 구현체를 생성자로 넣어서 사용

  • AuthenticationCredential
    • token값들을 가지고 있고, 만료시간 정보를 보고 refresh가 필요한지 판단하여 requiresRefresh 플래그값에 적용
import Alamofire

struct MyAuthenticationCredential: AuthenticationCredential {
    let accessToken: String
    let refreshToken: String
    let expiredAt: Date

    // 유효시간이 앞으로 5분 이하 남았다면 refresh가 필요하다고 true를 리턴 (false를 리턴하면 refresh 필요x)
    var requiresRefresh: Bool { Date(timeIntervalSinceNow: 60 * 5) > expiredAt }
}
  • Authenticator
    • Credential타입을 가지고, 이 타입의 token을 가지고 refresh하기까지 4단계 수행
    • apply()
    • didRequest()
    • isRequest()
    • refresh()

  • Authenticator - apply()
    • api요청 시 AuthenticatorIndicator객체가 존재하면, 요청 전에 가로채서 apply에서 Header에 bearerToken 추가
func apply(_ credential: Credential, to urlRequest: inout URLRequest) {
    urlRequest.headers.add(.authorization(bearerToken: credential.accessToken))
    urlRequest.addValue(credential.refreshToken, forHTTPHeaderField: "refresh-token")
}
  • Authenticator - didRequest
    • api요청 후 error가 떨어진 경우, 401에러(인증에러)인 경우만 refresh가 되도록 필터링
func didRequest(_ urlRequest: URLRequest, with response: HTTPURLResponse, failDueToAuthenticationError error: Error) -> Bool {
    return response.statusCode == 401
}
  • Authenticator - isRequest
    • 인증이 필요한 urlRequest에 대해서만 refresh가 되도록, 이 경우에만 true를 리턴하여 refresh 요청
func isRequest(_ urlRequest: URLRequest, authenticatedWith credential: Credential) -> Bool {
    // bearerToken의 urlRequest대해서만 refresh를 시도 (true)
    let bearerToken = HTTPHeader.authorization(bearerToken: credential.accessToken).value
    return urlRequest.headers["Authorization"] == bearerToken
}
  • Authenticator - refresh
    • token을 refresh하는 부분
func refresh(_ credential: Credential, for session: Session, completion: @escaping (Result<Credential, Error>) -> Void) {
    // TODO: refresh API 콜
    /*
     switch result {
     case .success(let response):
        completion(.success(credential))
     case .failure(let error):
        completion(.failure(error))
     }
     */
}

사용

  • 위에서 만든 AuthenticationCredential과 Authenticator를 준수한 인스턴스를 AuthenticatorInterceptor의 생성자에 넣고,
    AF.request(_:interceptor)에 삽입
/// 이름과 패스워드로 로그인 + AuthencitationInterceptor 사용
static func predictWithAuth(request: PredictAgeRequest, completion: @escaping (_ succeed: Person?, _ failed: Error?) -> Void) {

    // AuthenticationInterceptor 적용
    let authenticator = MyAuthenticator()
    let credential = MyAuthenticationCredential(accessToken: KeychainServiceImpl.shared.accessToken ?? "",
                                                refreshToken: KeychainServiceImpl.shared.refreshToken ?? "",
                                                expiredAt: Date(timeIntervalSinceNow: 60 * 120))
    let myAuthencitationInterceptor = AuthenticationInterceptor(authenticator: authenticator,
                                                                credential: credential)

    AF.request(PredictAgeTarget.predict(request), interceptor: myAuthencitationInterceptor)
        .responseDecodable { (response: AFDataResponse<PredictAgeResponse>) in
            switch response.result {
            case .success(let response):
                completion(response.toDomain, nil)
            case .failure(let error):
                completion(nil, error)
            }
        }
}

* 전체 소스코드

- https://github.com/JK0369/ExAlamofire

 

* 참고

- https://github.com/Alamofire/Alamofire/blob/master/Documentation/AdvancedUsage.md#adapting-and-retrying-requests-with-requestinterceptor

Comments