-
Notifications
You must be signed in to change notification settings - Fork 0
[week14] KeyChain
Rhode edited this page Apr 9, 2023
·
1 revision
import UIKit
class LogInViewController: UIViewController {
@IBOutlet weak var pwTextField: UITextField!
var diaryViewController: DiaryViewController?
override func viewDidLoad() {
super.viewDidLoad()
diaryViewController = self.storyboard?.instantiateViewController(withIdentifier: "diary") as? DiaryViewController
}
@IBAction func tapLogInButton(_ sender: Any) {
guard let diaryViewController = diaryViewController else { return }
let storedPW = UserDefaults.standard.string(forKey: "passwordKey")
guard pwTextField.text == storedPW else {
let alert = UIAlertController(title: "비밀번호가 일치하지 않습니다", message: "등록된 비밀번호와 입력한 비밀번호가 일치하지 않습니다", preferredStyle: .alert)
let confirm = UIAlertAction(title: "돌아가기", style: .default)
alert.addAction(confirm)
present(alert, animated: true, completion: nil)
return
}
diaryViewController.modalPresentationStyle = .fullScreen
present(diaryViewController, animated: true)
}
@IBAction func addNewPassword(_ sender: Any) {
let passwordRegex = "^(?=.*[A-Za-z])(?=.*[0-9])(?=.*[!@#$%^&*()_+=-]).{8,16}"
let validPw = pwTextField.text?.range(of: passwordRegex, options: .regularExpression) != nil
if validPw {
guard let newPassword = pwTextField.text, !newPassword.isEmpty else { return }
UserDefaults.standard.set(newPassword, forKey: "passwordKey")
let alert = UIAlertController(title: "성공", message: "비밀번호가 성공적으로 등록되었습니다.", preferredStyle: .alert)
let confirm = UIAlertAction(title: "확인", style: .default)
alert.addAction(confirm)
present(alert, animated: true, completion: nil)
} else {
let alert = UIAlertController(title: "비밀번호 오류", message: "영어 대소문자, 숫자, 특수기호를 넣어 8자리 이상 16자리 이하 비밀번호를 만들어주세요.", preferredStyle: .alert)
let confirm = UIAlertAction(title: "확인", style: .default)
alert.addAction(confirm)
present(alert, animated: true, completion: nil)
}
}
}
KeyChainManager:
import Foundation
final class KeychainManager {
static let shared = KeychainManager()
private init() {}
func createKeychain(credentials: Credentials) throws {
let account = credentials.userName
let password = credentials.password.data(using: String.Encoding.utf8)!
let query: [String: Any] = [kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: account,
kSecValueData as String: password]
let status = SecItemAdd(query as CFDictionary, nil)
guard status != errSecDuplicateItem else {
throw KeychainError.duplicateItem
}
guard status == errSecSuccess else {
throw KeychainError.unhandledError(status: status)
}
}
func readKeyChain(account: String) throws -> Credentials {
let query: [String: Any] = [kSecClass as String: kSecClassGenericPassword,
kSecMatchLimit as String: kSecMatchLimitOne,
kSecAttrAccount as String: account,
kSecReturnAttributes as String: true,
kSecReturnData as String: true]
var item: CFTypeRef?
let status = SecItemCopyMatching(query as CFDictionary, &item)
guard status != errSecItemNotFound else {
throw KeychainError.noPassword
}
guard status == errSecSuccess else {
throw KeychainError.unhandledError(status: status)
}
guard let existingItem = item as? [String: Any],
let passwordData = existingItem[kSecValueData as String] as? Data,
let password = String(data: passwordData, encoding: String.Encoding.utf8),
let account = existingItem[kSecAttrAccount as String] as? String else {
throw KeychainError.unexpectedPasswordData
}
let credentials = Credentials(userName: account, password: password)
return credentials
}
func updateKeyChain(credentials: Credentials) throws {
let account = credentials.userName
let password = credentials.password.data(using: String.Encoding.utf8)!
let query: [String: Any] = [kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: account]
let attributes: [String: Any] = [kSecValueData as String: password]
let status = SecItemUpdate(query as CFDictionary, attributes as CFDictionary)
guard status != errSecDuplicateItem else {
throw KeychainError.duplicateItem
}
guard status != errSecItemNotFound else {
throw KeychainError.noPassword
}
guard status == errSecSuccess else {
throw KeychainError.unhandledError(status: status)
}
}
}
LogInViewController:
import UIKit
class LogInViewController: UIViewController {
let account = "Rhode"
let keyChainManager = KeychainManager.shared
let alertManager = AlertManager.shared
@IBOutlet weak var pwTextField: UITextField!
var diaryViewController: DiaryViewController?
var changePasswordViewController: ChangePasswordViewController?
override func viewDidLoad() {
super.viewDidLoad()
diaryViewController = self.storyboard?.instantiateViewController(withIdentifier: "diary") as? DiaryViewController
changePasswordViewController = self.storyboard?.instantiateViewController(identifier: "newPW") as? ChangePasswordViewController
}
@IBAction func tapLogInButton(_ sender: Any) {
do {
let loaded = try keyChainManager.readKeyChain(account: account)
guard loaded.password == pwTextField.text else {
alertManager.presentAlert(viewController: self, title: "비밀번호 불일치", message: "등록된 비밀번호와 입력한 비밀번호가 일치하지 않습니다.")
return
}
} catch {
alertManager.presentAlert(viewController: self, title: "에러", message: "\(error)")
}
guard let diaryViewController = diaryViewController else { return }
diaryViewController.modalPresentationStyle = .fullScreen
present(diaryViewController, animated: true)
}
@IBAction func addNewPassword(_ sender: Any) {
let passwordRegex = "^(?=.*[A-Za-z])(?=.*[0-9])(?=.*[!@#$%^&*()_+=-]).{8,16}"
let validPw = pwTextField.text?.range(of: passwordRegex, options: .regularExpression) != nil
if validPw {
guard let newPassword = pwTextField.text, !newPassword.isEmpty else {
alertManager.presentAlert(viewController: self, title: "비밀번호 입력 요망", message: "비밀번호를 입력해주세요.")
return
}
let credentials = Credentials(userName: account, password: newPassword)
do {
try keyChainManager.createKeychain(credentials: credentials)
alertManager.presentAlert(viewController: self, title: "성공", message: "비밀번호가 성공적으로 등록되었습니다.")
} catch KeychainError.duplicateItem {
alertManager.presentAlert(viewController: self, title: "중복된 계정", message: "이미 계정에 비밀번호가 있습니다.")
} catch {
alertManager.presentAlert(viewController: self, title: "에러", message: "\(error)")
}
} else {
alertManager.presentAlert(viewController: self, title: "비밀번호 오류", message: "영어 대소문자, 숫자, 특수기호를 넣어 8자리 이상 16자리 이하 비밀번호를 만들어주세요.")
return
}
}
@IBAction func tapChangePasswordButton(_ sender: UIButton) {
guard let changePasswordViewController = changePasswordViewController else { return }
changePasswordViewController.modalPresentationStyle = .popover
present(changePasswordViewController, animated: true)
}
}
ChangePasswordViewController:
import UIKit
class ChangePasswordViewController: UIViewController {
let account = "Rhode"
let keyChainManager = KeychainManager.shared
let alertManager = AlertManager.shared
@IBOutlet weak var currentPWTextField: UITextField!
@IBOutlet weak var newPWTextField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
}
@IBAction func tapChangePasswordButton(_ sender: Any) {
do {
let loaded = try keyChainManager.readKeyChain(account: account)
guard loaded.password == currentPWTextField.text else {
alertManager.presentAlert(viewController: self, title: "비밀번호 불일치", message: "등록된 비밀번호와 입력한 비밀번호가 일치하지 않습니다.")
return
}
} catch {
alertManager.presentAlert(viewController: self, title: "에러", message: "\(error)")
}
do {
let passwordRegex = "^(?=.*[A-Za-z])(?=.*[0-9])(?=.*[!@#$%^&*()_+=-]).{8,16}"
let validPw = newPWTextField.text?.range(of: passwordRegex, options: .regularExpression) != nil
if validPw {
guard let newPassword = newPWTextField.text, !newPassword.isEmpty else {
alertManager.presentAlert(viewController: self, title: "비밀번호 입력 요망", message: "비밀번호를 입력해주세요.")
return
}
let credentials = Credentials(userName: account, password: newPassword)
try keyChainManager.updateKeyChain(credentials: credentials)
alertManager.presentAlert(viewController: self, title: "비밀번호 변경 성공", message: "비밀번호가 변경되었습니다.") {
self.dismiss(animated: true)
}
} else {
alertManager.presentAlert(viewController: self, title: "비밀번호 오류", message: "영어 대소문자, 숫자, 특수기호를 넣어 8자리 이상 16자리 이하 비밀번호를 만들어주세요.")
}
} catch {
alertManager.presentAlert(viewController: self, title: "에러", message: "\(error)")
}
}
}