ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [iOS] 안전한 로그인을 위한 키체인(Keychain)과 FaceID/TouchID - 원문 해석본
    I'm a Developer/iOS & macOS 2020. 2. 4. 15:43

     

     

     

    본 글은 Tim Mitra라는 개발자의 "How To Secure iOS User Data: The Keychain and Biometrics – Face ID or Touch ID"

    라는 주제의 글입니다.

     

    iOS에서 유저의 데이터를 안전하게 관리하기 위한 코드 작성에 대한 튜토리얼로 키체인과 생체정보(FaceID, TouchID)를 활용하여 구현하는 예제 입니다.

     

    본 글의 원문은 Raywenderlich에 있으니 정확한 text는 원문을 참고하시기 바랍니다.

     


     

    로그인 화면으로 앱을 보호하는 것은 사용자 데이터를 보호하는 좋은 방법입니다. iOS에 내장 된 키 체인을 사용하여 데이터를 안전하게 유지할 수 있습니다. Apple은 또한 Face ID 및 Touch ID로 또 다른 보호 계층을 제공합니다.

    iPhone 5S부터 사용 가능한 생체 인식 데이터는 A7 및 최신 칩의 안전한 엔 클레이브에 저장됩니다. 이 모든 것은 로그인 정보 처리 책임을 키 체인과 Face ID 또는 Touch ID로 편안하게 전달할 수 있음을 의미합니다.

    이 학습서에서는 정적 인증으로 시작합니다. 다음으로 키 체인을 사용하여 로그인 정보를 저장하고 확인합니다. 마지막으로 앱에서 Touch ID 또는 Face ID를 사용하여 탐색합니다.

     

     

    [참고]
    1. Face ID는 실제 장치에서 테스트해야합니다. 
    2. 터치 ID는 이제 시뮬레이터의 Xcode 9에서 에뮬레이션 할 수 있습니다.
    3. 키 체인은 시뮬레이터에서도 사용할 수 있습니다.
    4. 튜토리얼 전체에서 저는 Touch ID를 참조하며 대부분의 경우 Face ID에 적용됩니다. 
    5. 코드 밑에는, 로컬 인증 프레임 워크가 있습니다.

    시작하기


    이 튜토리얼의 스타터 프로젝트를 여기에서 다운로드 하십시오.

    Core Data를 사용하여 사용자 메모를 저장하는 기본 메모 작성 앱입니다. 스토리 보드에는 사용자가 사용자 이름과 비밀번호를 입력 할 수있는 로그인보기가 있으며 나머지 앱보기는 이미 서로 연결되어 있으며 사용할 준비가되었습니다.

    현재 상태에서 앱이 어떻게 보이는지 확인하고 빌드하십시오.

     

    [참고]
    노트 유형 누락에 대한 컴파일러 오류는 무시할 수 있습니다. 
    핵심 데이터에 의해 자동 생성됩니다

     

     

    이 시점에서 로그인 버튼을 누르면보기가 사라지고 메모 목록이 표시됩니다.이 화면에서 새 메모를 작성할 수도 있습니다. 로그 아웃을 누르면 로그인보기로 돌아갑니다. 앱이 백그라운드로 푸시되면 즉시 로그인보기로 돌아갑니다. 이렇게하면 로그인하지 않고 데이터를 볼 수 없습니다.

    다른 작업을하기 전에 번들 식별자를 변경하고 적절한 팀을 지정해야합니다.

    프로젝트 탐색기에서 TouchMeIn을 선택한 다음 TouchMeIn 대상을 선택하십시오. 일반 탭에서 번들 식별자를 역 도메인 표기법으로 사용하도록 변경하십시오 (예 : com.raywenderich.TouchMeIn).

    그런 다음 팀 메뉴에서 개발자 계정과 관련된 팀을 다음과 같이 선택하십시오.


    모든 하우스 키핑이 완료되면 코딩 할 시간입니다! :]

     

    Logging? No. Log In.

    공을 굴리기 위해 하드 코딩 된 값과 비교하여 사용자 제공 자격 증명을 확인하는 기능을 추가합니다.
    LoginViewController.swift를 열고 managedObjectContext 바로 아래에 다음 상수를 추가하십시오 :

    let usernameKey = "Batman"
    let passwordKey = "Hello Bruce!"
    

    이는 사용자가 제공 한 자격 증명을 확인할 하드 코드 된 사용자 이름과 비밀번호입니다.

    다음으로 loginAction (_ :) 아래에 다음 메소드를 추가하십시오.

    func checkLogin(username: String, password: String) -> Bool {
      return username == usernameKey && password == passwordKey
    }
    

    여기서 checkLogin (username : password :)을 호출하면 자격 증명이 올바른 경우에만 로그인보기가 해제됩니다.

    빌드하고 실행하십시오. 사용자 이름 Batman과 비밀번호 Hello Bruce!를 입력하고 로그인 버튼을 누릅니다. 로그인 화면이 예상대로 사라져야합니다.

     

    인증에 대한이 간단한 접근 방식은 효과가있는 것처럼 보이지만, 적절한 도구와 교육을 통해 호기심 많은 해커가 문자열로 저장된 자격 증명을 쉽게 손상시킬 수 있기 때문에별로 안전하지 않습니다. 모범 사례로 비밀번호를 앱에 직접 저장해서는 안됩니다. 이를 위해 키 체인을 사용하여 비밀번호를 저장합니다.

     

    [참고]
    대부분의 앱에서 비밀번호는 단순히 문자열이며 글 머리 기호로 숨겨져 있습니다. 
    앱에서 비밀번호를 처리하는 가장 좋은 방법은 비밀번호를 캡처하자마자 SALT 암호화로 암호화하거나 암호화하는 것입니다.
    사용자 만이 실제 문자열을 알아야합니다. 이것은이 학습서의 범위를 벗어나지 만이 점을 명심해야합니다.

    Ryan Ackermann의 기본 iOS 보안 : 키 체인 및 해싱 자습서에서 키 체인의 작동 방식 및 비밀번호 솔팅에 대한 자세한 내용을 확인하십시오.

     

    Rapper? No. Wrapper.

    다음 단계는 앱에 키 체인 래퍼를 추가하는 것입니다.

    시작 프로젝트와 함께 유용한 리소스가있는 폴더를 다운로드했습니다. Finder에서 Resources 폴더를 찾아 엽니 다. KeychainPasswordItem.swift 파일이 표시됩니다. 이 클래스는 Apple의 샘플 코드 GenericKeychain에서 제공됩니다.

    KeychainPasswordItem.swift를 다음과 같이 프로젝트로 드래그하십시오.

    메시지가 표시되면 Copy items if neededTouchMeIn 대상이 모두 선택되어 있는지 확인하십시오.

     

     

    오류가 없는지 확인하고 빌드하십시오. 문제 없나요? 훌륭합니다. 이제 앱 내에서 키 체인을 활용할 수 있습니다.

     

    Keychain, Meet Password. Password, Meet Keychain

    키 체인을 사용하려면 먼저 사용자 이름과 비밀번호를 저장하십시오. 다음으로 키 체인에 대해 사용자 제공 자격 증명을 확인하여 일치하는지 확인합니다.

    로그인 버튼의 텍스트를 "만들기"에서 "로그인"으로 변경할 수 있도록 사용자가 이미 일부 자격 증명을 만들 었는지 추적합니다. 매번 키 체인을 누르지 않아도이 확인을 수행 할 수 있도록 사용자 기본값을 사용자 기본값으로 저장합니다.

    키 체인에는 앱 정보를 제대로 저장하기위한 구성이 필요합니다. serviceName 및 선택적 accessGroup 형식으로 해당 구성을 제공합니다. 구조체를 사용하여 이러한 값을 저장합니다.

    LoginViewController.swift를 여십시오. import 문 바로 아래에 다음을 추가하십시오.

     

    // Keychain Configuration
    struct KeychainConfiguration {
      static let serviceName = "TouchMeIn"
      static let accessGroup: String? = nil
    }
    

    그런 다음 managedObjectContext 아래에 다음을 추가하십시오.

    var passwordItems: [KeychainPasswordItem] = []
    let createLoginButtonTag = 0
    let loginButtonTag = 1
    
    @IBOutlet weak var loginButton: UIButton!
    

    passwordItems는 키 체인에 전달할 KeychainPasswordItem 유형의 빈 배열입니다. 다음 두 상수를 사용하여 로그인 버튼을 사용하여 자격 증명을 만들거나 로그인하는 데 사용되는지 확인합니다. loginButton 콘센트를 사용하여 상태에 따라 버튼 제목을 업데이트합니다.

    다음으로 버튼을 누를 때의 두 가지 경우를 처리합니다. 사용자가 아직 자격 증명을 만들지 않은 경우 버튼 텍스트에 "만들기"가 표시되고 그렇지 않으면 버튼에 "로그인"이 표시됩니다.

    먼저 로그인이 실패한 경우 사용자에게 알리는 방법이 필요합니다. checkLogin (username : password :) 뒤에 다음을 추가하십시오.

    private func showLoginFailedAlert() {
      let alertView = UIAlertController(title: "Login Problem",
                                        message: "Wrong username or password.",
                                        preferredStyle:. alert)
      let okAction = UIAlertAction(title: "Foiled Again!", style: .default)
      alertView.addAction(okAction)
      present(alertView, animated: true)
    }
    

    이제 loginAction (sender :)을 다음으로 바꾸십시오.

    @IBAction func loginAction(sender: UIButton) {
      // 1
      // Check that text has been entered into both the username and password fields.
      guard let newAccountName = usernameTextField.text,
        let newPassword = passwordTextField.text,
        !newAccountName.isEmpty,
        !newPassword.isEmpty else {
          showLoginFailedAlert()
          return
      }
        
      // 2
      usernameTextField.resignFirstResponder()
      passwordTextField.resignFirstResponder()
        
      // 3
      if sender.tag == createLoginButtonTag {
        // 4
        let hasLoginKey = UserDefaults.standard.bool(forKey: "hasLoginKey")
        if !hasLoginKey && usernameTextField.hasText {
          UserDefaults.standard.setValue(usernameTextField.text, forKey: "username")
        }
          
        // 5
        do {
          // This is a new account, create a new keychain item with the account name.
          let passwordItem = KeychainPasswordItem(service: KeychainConfiguration.serviceName,
                                                  account: newAccountName,
                                                  accessGroup: KeychainConfiguration.accessGroup)
            
          // Save the password for the new item.
          try passwordItem.savePassword(newPassword)
        } catch {
          fatalError("Error updating keychain - \(error)")
        }
          
        // 6
        UserDefaults.standard.set(true, forKey: "hasLoginKey")
        loginButton.tag = loginButtonTag
        performSegue(withIdentifier: "dismissLogin", sender: self)
      } else if sender.tag == loginButtonTag {
         // 7
        if checkLogin(username: newAccountName, password: newPassword) {
          performSegue(withIdentifier: "dismissLogin", sender: self)
        } else {
          // 8
          showLoginFailedAlert()
        }
      }
    }
    

    코드에서 일어나는 일은 다음과 같습니다.

    1. 사용자 이름 또는 비밀번호가 비어 있으면 사용자에게 경고를 표시하고 메소드에서 돌아옵니다.
    2. 키보드가 보이면 닫습니다.
    3. 로그인 버튼의 태그가 createLoginButtonTag이면 새 로그인을 생성하십시오.
    4. 다음으로, UserDefaults에서 hasLoginKey를 읽고 암호가 키 체인에 저장되었는지 여부를 나타냅니다. hasLoginKey가 false이고 username 필드에 텍스트가 있으면 해당 텍스트를 username으로 UserDefaults에 저장합니다.
    5. serviceNamenewAccountName (사용자 이름) 및 accessGroup을 사용하여 KeychainPasswordItem을 만듭니다. Swift의 오류 처리를 사용하여 비밀번호를 저장하려고합니다. 무언가 잘못되면 catch가 동작합니다.
    6. 그런 다음 UserDefaults에서 hasLoginKey를 true로 설정하여 암호가 키 체인에 저장되었음을 나타냅니다. 로그인 버튼의 태그를 loginButtonTag로 설정하여 버튼의 텍스트를 변경하면 사용자에게 로그인을 만들라는 메시지를 표시하지 않고 다음에 앱을 실행할 때 로그인하라는 메시지가 표시됩니다. 마지막으로 loginView를 닫습니다.
    7. 사용자가 로그인 한 경우 (loginButtonTag로 표시됨) checkLogin을 호출하여 사용자 제공 자격 증명을 확인합니다. 일치하면 로그인보기를 닫습니다.
    8. 로그인 인증이 실패하면 사용자에게 경고 메시지를 표시하십시오.

    [참고]
    UserDefaults에 사용자 이름과 함께 비밀번호를 저장하지 않는 이유는 무엇입니까? 
    UserDefaults에 저장된 값은 plist 파일을 사용하여 유지되기 때문에 나쁜 생각입니다. 
    이 파일은 기본적으로 앱의 라이브러리 폴더에있는 XML 파일이므로 
    기기에 실제로 액세스 할 수있는 모든 사람이 읽을 수 있습니다. 
    반면에 키 체인은 3DES (Triple Digital Encryption Standard)를 사용하여 데이터를 암호화합니다. 
    누군가가 데이터를 가져 오더라도 읽을 수 없습니다.

    그런 다음 checkLogin (username : password :)을 다음 업데이트 된 구현으로 바꾸십시오.

    func checkLogin(username: String, password: String) -> Bool {
      guard username == UserDefaults.standard.value(forKey: "username") as? String else {
        return false
      }
        
      do {
        let passwordItem = KeychainPasswordItem(service: KeychainConfiguration.serviceName,
                                                account: username,
                                                accessGroup: KeychainConfiguration.accessGroup)
        let keychainPassword = try passwordItem.readPassword()
        return password == keychainPassword
      } catch {
        fatalError("Error reading password from keychain - \(error)")
      }
    }
    

    여기에서 입력 한 사용자 이름이 UserDefaults에 저장된 것과 일치하는지, 암호가 키 체인에 저장된 것과 일치하는지 확인하십시오.

    다음 줄을 삭제하십시오.

    let usernameKey = "Batman"
    let passwordKey = "Hello Bruce!"
    

    이제 hasLoginKey 상태에 따라 버튼 제목과 태그를 적절하게 설정해야합니다.

    super 호출 바로 아래에 viewDidLoad ()에 다음 코드를 추가하십시오.

    // 1
    let hasLogin = UserDefaults.standard.bool(forKey: "hasLoginKey")
        
    // 2
    if hasLogin {
      loginButton.setTitle("Login", for: .normal)
      loginButton.tag = loginButtonTag
      createInfoLabel.isHidden = true
    } else {
      loginButton.setTitle("Create", for: .normal)
      loginButton.tag = createLoginButtonTag
      createInfoLabel.isHidden = false
    }
        
    // 3
    if let storedUsername = UserDefaults.standard.value(forKey: "username") as? String {
      usernameTextField.text = storedUsername
    }
    

    번호가 매겨진 각 주석을 차례대로 설명하자면.
    1. 먼저 "로그인 키"를 확인하여이 사용자의 로그인을 저장했는지 확인하십시오.
    2. 그렇다면 단추 제목을 "로그인"으로 변경하고 태그를 "loginButtonTag"로 업데이트 한 다음 "사용자 이름 및 암호를 작성하여 시작"이라는 정보 텍스트가 포함 된 createcreateLabel을 숨 깁니다. 이 사용자에 대한 로그인이 저장되어 있지 않은 경우 버튼 레이블을 "만들기"로 설정하고 "정보 표시창 만들기"를 사용자에게 표시합니다.
    3. 마지막으로 사용자 이름 필드를 "UserDefaults (사용자 기본값)"에 저장된 내용으로 설정하여 사용자가 좀 더 편리하게 로그인 할 수 있도록합니다.

     

    마지막으로, 콘센트를 로그인 버튼에 연결해야합니다. Main.storyboard를 열고 Login View Controller Scene을 선택하십시오. 아래와 같이 Login View Controller에서 Login 버튼으로 Ctrl + Drag하십시오.

    결과 팝업에서 loginButton을 선택하십시오.

    빌드하고 실행하십시오. 원하는 사용자 이름과 비밀번호를 입력 한 다음 생성을 탭하십시오.

    [참고]
    loginButton IBOutlet 연결을 잊어 버린 경우 다음과 같은 에러를 볼 수 있습니다.
    Fatal error: unexpectedly found nil while unwrapping an Optional value.
    
    이럴 경우 위의 단계에 설명한대로 outlet을 연결해보세요.

    이제 로그 아웃을 누르고 동일한 사용자 이름과 비밀번호로 로그인을 시도하십시오. 메모 목록이 나타납니다.

    로그 아웃을 누르고 다시 로그인하십시오. 이번에는 다른 비밀번호를 사용하고 로그인을 누릅니다. 다음과 같은 경고가 나타납니다.

     

    축하합니다

    이제 키 체인을 사용하여 인증을 추가했습니다. 다음은 '터치 ID'입니다.

     

    Touching You, Touching Me

    [참고]
    Face ID를 사용하려면 iPhone X와 같은 물리적 장치에서 테스트해야합니다. 
    이제 시뮬레이터의 Xcode 9에서 Touch ID를 에뮬레이션 할 수 있습니다. 
    A7 칩 이상 및 Face ID / Touch ID 하드웨어가있는 모든 장치에서 생체 인식 ID를 테스트 할 수 있습니다.

    이 섹션에서는 키 체인을 사용하는 것 외에도 생체 인식 ID를 프로젝트에 추가합니다. Face ID / Touch ID가 작동하는 데 키 체인이 필요하지는 않지만 생체 인식 ID가 실패한 경우 또는 Touch ID 호환 장치가없는 사용자에게는 항상 백업 인증 방법을 구현하는 것이 좋습니다.

     

    Assets.xcassets를 open 합니다.

    그런 다음 이전에 Finder에서 다운로드 한 시작 프로젝트에서 Resources 폴더를 엽니 다. FaceIcon 및 Touch-icon-lg.png 이미지 (모두 세 가지 크기)를 찾아 6 개를 모두 선택한 다음 Images.xcassets로 드래그하여 Xcode가 다른 해상도에서만 동일한 이미지임을 알 수 있도록합니다.

     

    Main.storyboard를 열고 Object Library의 버튼을 Stack View 내부의 Create Info Label 바로 아래의 Login View Controller Scene으로 드래그합니다. 문서 개요를 열고 펼침 삼각형을 연 다음 단추가 스택보기 안에 있는지 확인하십시오. 다음과 같아야합니다.

     

    스택 뷰를 검토해야하는 경우 UIStackView 자습서 : 스택 뷰 소개를 살펴보십시오.
    속성 관리자에서 다음과 같이 버튼의 속성을 조정합니다.

     

    • 유형을 사용자 정의로 설정하십시오.
    • 제목을 비워 두십시오.
    • 이미지를 Touch-icon-lg로 설정하십시오.

    완료되면 버튼의 속성은 다음과 같아야합니다.

    새 버튼이 선택되어 있는지 확인한 다음 스토리 보드 캔버스의 맨 아래에있는 레이아웃 막대에서 새 제약 조건 추가 버튼을 클릭하고 다음과 같이 제약 조건을 설정하십시오.

    • Width = 66
    • Height = 67

    구속 조건 추가를 클릭합니다. 뷰는 이제 다음과 같아야합니다.

    여전히 Main.storyboard에서 Assistant Editor를 열고 LoginViewController.swift가 표시되는지 확인하십시오.

    다음으로 방금 추가 한 버튼에서 다른 IBOutlets 바로 아래에있는 LoginViewController.swift에 Control- 드래그하십시오.

    팝업에서 이름으로 touchIDButton을 입력하고 연결을 클릭하십시오.

    생체 인식 ID가없는 기기에서 버튼을 숨기는 데 사용할 IBOutlet이 생성됩니다.

    다음으로 단추에 대한 조치를 추가해야합니다.

    checkLogin (username : password :) 바로 위에있는 동일한 버튼에서 LoginViewController.swift로 Control + Drag하십시오.

     

    팝업에서 Connection을 Action으로 변경하고 Name을 touchIDLoginAction으로 설정하고 Arguments를 none으로 설정하십시오. 그런 다음 연결을 클릭하십시오.

    오류를 확인하기 위해 빌드하고 실행하십시오. 아직 생체 인식 ID에 대한 지원을 추가하지 않았으므로이 시점에서 시뮬레이터를 빌드 할 수 있습니다. 이제 처리하겠습니다.

     

    Adding Local Authentication

    생체 인식 ID 구현은 로컬 인증 프레임 워크를 가져오고 간단하지만 강력한 몇 가지 방법을 호출하는 것만 큼 간단합니다.

    로컬 인증 설명서의 내용은 다음과 같습니다.

    "로컬 인증 프레임 워크는 지정된 보안 정책을 가진 사용자에게 인증을 요청하는 기능을 제공합니다."

    이 경우 지정된 보안 정책은 사용자의 생체 인식, 즉 얼굴 또는 지문입니다. :]

    iOS 11의 새로운 기능은 Face ID를 지원합니다. LocalAuthentication은 장치가 Face ID를 지원하는지 또는 Touch ID를 지원하는지 결정하기 위해 필요한 FaceIDUsageDescription 및 LABiometryType이라는 두 가지 새로운 기능을 추가합니다.

    Xcode의 프로젝트 탐색기에서 프로젝트를 선택하고 정보 탭을 클릭하십시오. 키 중 하나의 오른쪽 가장자리를 가리키고 +를 클릭하십시오. "Privacy"를 입력하기 시작하고 나타나는 팝업 목록에서 "Privacy – Face ID Usage Description"을 키로 선택하십시오.

    [참고]
    Key로 "NSFaceIDUsageDescription"을 입력 할 수도 있습니다.
    

    유형은 문자열이어야합니다. 값 필드에 얼굴 ID를 사용하여 메모 잠금을 해제하십시오를 입력하십시오.

    프로젝트 탐색기에서 TouchMeIn 그룹 폴더를 마우스 오른쪽 단추로 클릭하고 새 파일…을 선택하십시오. iOS \ Swift File을 선택하십시오. 다음을 클릭하십시오. TouchMeIn 대상이 선택된 상태에서 파일을 TouchIDAuthentication.swift로 저장하십시오. 작성을 클릭하십시오.

    TouchIDAuthentication.swift를 열고 import Foundation 바로 아래에 다음 가져 오기를 추가하십시오

    import LocalAuthentication
    

    다음으로 새 클래스를 만들려면 다음을 추가하십시오.

    class BiometricIDAuth {
      
    }
    

    이제 LAContext 클래스에 대한 참조가 필요합니다.

    클래스 안에 중괄호 사이에 다음 코드를 추가하십시오.

    let context = LAContext()
    

    컨텍스트는 로컬 인증의 주 플레이어 인 인증 컨텍스트를 참조합니다. 사용자 기기 또는 시뮬레이터에서 생체 인식 ID를 사용할 수 있는지 확인하는 기능이 필요합니다.

    BiometricIDAuth 내에서 생체 인식 ID가 지원되는 경우 Bool을 반환하려면 다음 방법을 추가하십시오.

    func canEvaluatePolicy() -> Bool {
      return context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: nil)
    }
    

    LoginViewController.swift를 열고 다음 속성을 추가하여 BiometricIDAuth에 대한 참조를 만듭니다.

     

    let touchMe = BiometricIDAuth()
    

    viewDidLoad ()의 맨 아래에 다음을 추가하십시오.

     

    touchIDButton.isHidden = !touchMe.canEvaluatePolicy()
    

    여기서 canEvaluatePolicy (_ :)를 사용하여 장치가 생체 인증을 구현할 수 있는지 확인합니다. 그렇다면 Touch ID 버튼을 표시하십시오. 그렇지 않은 경우 숨겨 두십시오.

    시뮬레이터에서 빌드하고 실행하십시오. Touch ID 로고가 숨겨져 있습니다. 이제 실제 Face ID / Touch ID 지원 장치에서 빌드하고 실행하십시오. Touch ID 버튼이 표시됩니다. 시뮬레이터의 하드웨어 메뉴에서 터치 ID> 등록을 선택하고 버튼을 테스트 할 수 있습니다.

     

    Face ID or Touch ID

    iPhone X 이상 Face ID가 장착 된 장치에서 실행중인 경우 문제가 발생할 수 있습니다. Face ID 사용 설명을 처리했는데 Touch ID 아이콘이 제자리에없는 것 같습니다. biometryType 열거 형을 사용하여이 문제를 해결합니다.

    TouchIDAuthentication.swift를 열고 클래스 위에 BiometricType 열거를 추가하십시오.

    enum BiometricType {
      case none
      case touchID
      case faceID
    }
    

    그런 다음 canEvaluatePolicy를 사용하여 지원되는 생체 인식 유형을 반환하려면 다음 함수를 추가하십시오.

    func biometricType() -> BiometricType {
      let _ = context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: nil)
      switch context.biometryType {
      case .none:
        return .none
      case .touchID:
        return .touchID
      case .faceID:
        return .faceID
      }
    }
    

    LoginViewController를 열고 viewDidLoad () 하단에 다음을 추가하여 버튼의 아이콘을 수정하십시오.

    switch touchMe.biometricType() {
    case .faceID:
      touchIDButton.setImage(UIImage(named: "FaceIcon"),  for: .normal)
    default:
      touchIDButton.setImage(UIImage(named: "Touch-icon-lg"),  for: .normal)
    }

    Touch ID가 등록 된 시뮬레이터에서 빌드하고 실행하여 Touch ID 아이콘을 확인하십시오. iPhone X – Face ID 아이콘에 올바른 아이콘이 표시됩니다.

     

    Putting Touch ID to Work

    TouchIDAuthentication.swift를 열고 컨텍스트 아래에 다음 변수를 추가하십시오.

    var loginReason = "Logging in with Touch ID"

    위는 애플리케이션이 인증을 요청하는 이유를 제공합니다. 제시된 대화 상자에서 사용자에게 표시됩니다.

    그런 다음 BiometricIDAuth의 맨 아래에 다음 방법을 추가하여 사용자를 인증하십시오.

     

    func authenticateUser(completion: @escaping () -> Void) { // 1
      // 2
      guard canEvaluatePolicy() else {
        return
      }
    
      // 3
      context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics,
        localizedReason: loginReason) { (success, evaluateError) in
          // 4
          if success {
            DispatchQueue.main.async {
              // User authenticated successfully, take appropriate action
              completion()
            }
          } else {
            // TODO: deal with LAError cases
          }
      }
    }
    

    위 코드에서 진행중인 작업은 다음과 같습니다.
    1.  authenticateUser (completion :) 클로저 형태로 완료 핸들러를 사용합니다.
    2. 'canEvaluatePolicy ()를 사용하여 기기가 생체 인증을 수행 할 수 있는지 확인하고 있습니다.
    3. 장치가 생체 인식 ID를 지원하는 경우 "evaluatePolicy (_ : localizedReason : reply :)"를 사용하여 정책 평가를 시작합니다. 즉, 사용자에게 생체 인식 ID 인증을 요구합니다. evaluationPolicy (_ : localizedReason : reply :) 평가가 완료된 후 실행되는 응답 블록을받습니다.
    4. 답장 블록 안에서 성공 사례를 먼저 처리합니다. 기본적으로 정책 평가는 개인 스레드에서 수행되므로 코드가 기본 스레드로 다시 이동하여 UI를 업데이트 할 수 있습니다. 인증에 성공하면 로그인보기를 닫는 segue를 호출합니다.
    잠시 후 다시 와서 오류를 처리합니다.
    LoginViewController.swift를 열고 touchIDLoginAction (_ :)을 찾아 다음으로 바꿉니다.

     

    @IBAction func touchIDLoginAction() {    
      touchMe.authenticateUser() { [weak self] in
        self?.performSegue(withIdentifier: "dismissLogin", sender: self)
      }
    }
    

    사용자가 인증 된 경우 로그인보기를 닫을 수 있습니다.

    계속해서 빌드하고 실행하여 모든 것이 잘되는지 확인하십시오.

     

    Dealing with Errors

    기다림! 기기에서 생체 인식 ID를 설정하지 않으면 어떻게 되나요? 잘못된 손가락을 사용하거나 변장을하고 있다면 어떨까요? 그걸 다루 자.

    로컬 인증의 중요한 부분은 오류에 응답하므로 프레임 워크에 LAError 유형이 포함됩니다. canEvaluatePolicy의 두 번째 사용으로 인해 오류가 발생할 수도 있습니다.

    사용자에게 무엇이 잘못되었는지 알리는 경고가 표시됩니다. TouchIDAuth 클래스에서 LoginViewController로 메시지를 전달해야합니다. 다행히 완료 메시지를 사용하여 선택적 메시지를 전달할 수 있습니다.

    TouchIDAuthentication.swift를 열고 authenticateUser 메소드를 업데이트하십시오.

    오류가 발생했을 때 전달할 선택적 메시지를 포함하도록 서명을 변경하십시오.

    func authenticateUser(completion: @escaping (String?) -> Void) {
    

    다음으로 // TODO :를 찾아서 다음으로 바꿉니다.

    // 1
    let message: String
                
    // 2
    switch evaluateError {
    // 3
    case LAError.authenticationFailed?:
      message = "There was a problem verifying your identity."
    case LAError.userCancel?:
      message = "You pressed cancel."
    case LAError.userFallback?:
      message = "You pressed password."
    case LAError.biometryNotAvailable?:
      message = "Face ID/Touch ID is not available."
    case LAError.biometryNotEnrolled?:
      message = "Face ID/Touch ID is not set up."
    case LAError.biometryLockout?:
      message = "Face ID/Touch ID is locked."
    default:
      message = "Face ID/Touch ID may not be configured"
    }
    // 4
    completion(message)
    

    여기서는 무슨일이 일어났나요?

    1. 메시지를 보유 할 문자열을 선언하십시오.
    2. 이제 “failure” 사례입니다. switch 문을 사용하여 각 오류 사례에 적절한 오류 메시지를 설정 한 다음 사용자에게 경고보기를 제공하십시오.
    3. 인증이 실패하면 일반 경고가 표시됩니다. 실제로, 반환 된 특정 오류 코드를 실제로 평가하고 해결해야합니다. 여기에는 다음 중 하나가 포함될 수 있습니다.

    • LAError.biometryNotAvailable : 기기가 얼굴 ID / 터치 ID와 호환되지 않습니다.
    • LAError.passcodeNotSet : Touch ID에 필요한 암호가 활성화되어 있지 않습니다
    • LAError.biometryNotEnrolled : 저장된 얼굴 또는 지문이 없습니다.
    • LAError.biometryLockout : 시도가 너무 많이 실패했습니다.

    4. completion 클로저에 메시지를 전달하십시오.

     

    iOS는 관련 경고를 사용하여 LAError.passcodeNotSet 및 LAError.biometryNotEnrolled에 자체적으로 응답합니다.

    처리해야 할 오류 사례가 하나 더 있습니다. guard 문의 else 블록 안에 다음을 추가하십시오.

    completion("Touch ID not available")

    마지막으로 업데이트해야 할 것은 성공 사례입니다. 완료에 nil이 포함되어있어 오류가 발생하지 않았 음을 나타냅니다. 첫 번째 성공 블록 안에 nil을 추가하십시오.

    completion(nil)
    

    이러한 변경을 완료하면 완성 된 방법은 다음과 같아야합니다.

    func authenticateUser(completion: @escaping (String?) -> Void) {
        
      guard canEvaluatePolicy() else {
        completion("Touch ID not available")
        return
      }
        
      context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics,
        localizedReason: loginReason) { (success, evaluateError) in
          if success {
            DispatchQueue.main.async {
              completion(nil)
            }
          } else {
                                  
            let message: String
                                  
            switch evaluateError {
            case LAError.authenticationFailed?:
              message = "There was a problem verifying your identity."
            case LAError.userCancel?:
              message = "You pressed cancel."
            case LAError.userFallback?:
              message = "You pressed password."
            case LAError.biometryNotAvailable?:
              message = "Face ID/Touch ID is not available."
            case LAError.biometryNotEnrolled?:
              message = "Face ID/Touch ID is not set up."
            case LAError.biometryLockout?:
              message = "Face ID/Touch ID is locked."
            default:
              message = "Face ID/Touch ID may not be configured"
            }
              
            completion(message)
          }
      }
    }
    
    [참고]
    이 새로운 오류 처리를 컴파일하면 더 이상 사용되지 않는 상수 사용에 대해 경고하는 세 가지 경고가 표시됩니다.
    이는 Apple이 Face ID에 대한 지원을 추가 한 방식과 Swift가 Objective-C 헤더 파일을 가져 오는 
    방식의 조합 때문입니다. 몇 가지 잠재적 인 해결 방법이 있지만“Swift-like”는 훨씬 적습니다. 
    Apple은 이 문제를 알고 있으며 향후에이 문제를 해결할 계획이므로 더 깨끗한 코드가 여기에 표시됩니다.

    LoginViewController.swift를 열고 touchIDLoginAction (_ :)을 다음과 같이 업데이트하십시오 :

    @IBAction func touchIDLoginAction() {
      // 1
      touchMe.authenticateUser() { [weak self] message in
        // 2
        if let message = message {
          // if the completion is not nil show an alert
          let alertView = UIAlertController(title: "Error",
                                            message: message,
                                            preferredStyle: .alert)
          let okAction = UIAlertAction(title: "Darn!", style: .default)
          alertView.addAction(okAction)
          self?.present(alertView, animated: true)
        } else {
          // 3
          self?.performSegue(withIdentifier: "dismissLogin", sender: self)
        }
      }
    }
    

    이 코드 스 니펫에서 수행하는 작업은 다음과 같습니다.
    1. 선택 메시지를 수락하도록 후행 마감을 업데이트했습니다. 생체 인식 ID가 작동하면 메시지가 없습니다.
    2. 메시지를 풀고 경고와 함께 표시하려면 "만약"을 사용하십시오.
    3. 여기서는 변경하지 않지만 메시지가 없으면 로그인보기를 닫을 수 있습니다.
    실제 장치에서 빌드 및 실행하고 Touch ID를 사용하여 로그인을 테스트하십시오.
    LAContext는 대부분의 무거운 리프팅을 처리하기 때문에 생체 인식 ID를 구현하는 것이 비교적 간단하다는 것이 밝혀졌습니다. 또한 동일한 앱에서 키 체인 및 생체 인식 ID 인증을 사용하여 사용자에게 Touch ID 지원 장치가없는 이벤트를 처리 할 수있었습니다.

    [참고] 
    Touch ID의 오류를 테스트하려는 경우 잘못된 손가락으로 로그인을 시도 할 수 있습니다. 
    5 번 그렇게하면 Touch ID가 비활성화되고 암호 인증이 필요합니다. 
    이렇게하면 낯선 사람이 장치의 다른 응용 프로그램에 침입하지 못하게됩니다. 
    설정-> 터치 ID 및 비밀번호로 이동하여 다시 활성화 할 수 있습니다.

     

    Look Mom! No Hands.

    iPhone X의 가장 멋진 점 중 하나는 화면을 터치하지 않고 Face ID를 사용하는 것입니다. Face ID를 트리거하는 데 사용할 수있는 버튼을 추가했지만 Face ID를 자동으로 트리거 할 수도 있습니다.

    LoginViewController.swift를 열고 viewDidLoad () 바로 아래에 다음 코드를 추가하십시오 :

    override func viewDidAppear(_ animated: Bool) {
      super.viewDidAppear(animated)
      let touchBool = touchMe.canEvaluatePolicy()
      if touchBool {
       touchIDLoginAction()
      }
    }
    

    위는 생체 인식 ID가 지원되는지 확인하고, 그렇다면 사용자를 시도하고 인증합니다.

    iPhone X 또는 Face ID가 장착 된 장치에서 빌드 및 실행하고 핸즈프리로 로깅을 테스트하십시오!

     

    Where to Go from Here?

    이 자습서에서 완성 된 샘플 응용 프로그램을 여기에서 다운로드 할 수 있습니다.

    이 자습서에서 생성 한 LoginViewController는 사용자 자격 증명을 관리해야하는 모든 앱에 대한 시작점을 제공합니다.

    사용자가 때때로 암호를 변경할 수 있도록 새보기 컨트롤러를 추가하거나 기존 LoginViewController를 수정할 수도 있습니다. 사용자의 생체 인식은 수명이 크게 변하지 않으므로 생체 인식 ID에는 필요하지 않습니다. :] 그러나 키 체인을 업데이트하는 방법을 만들 수 있습니다. 수정을 수락하기 전에 사용자에게 현재 비밀번호를 입력하라는 메시지가 표시됩니다.

    또한 Face ID를 사용할 때 사용자 이름 및 암호 필드와 로그인 버튼을 숨길 것을 권장합니다. 나는 당신을 위해 간단한 도전으로 남겨 두겠습니다.

    Apple의 공식 iOS 보안 가이드에서 iOS 앱 보안에 대한 자세한 내용을 읽을 수 있습니다.

    이 튜토리얼에 대해 질문이나 의견이 있으시면 언제든지 아래 토론에 참여하십시오!

    댓글

Designed by Tistory.