版本记录
版本号 | 时间 |
---|---|
V1.0 | 2018.12.01 星期六 |
前言
在这个信息爆炸的年代,特别是一些敏感的行业,比如金融业和银行卡相关等等,这都对
app
的安全机制有更高的需求,很多大公司都有安全 部门,用于检测自己产品的安全性,但是及时是这样,安全问题仍然被不断曝出,接下来几篇我们主要说一下app
的安全机制。感兴趣的看我上面几篇。
1. APP安全机制(一)—— 几种和安全性有关的情况
2. APP安全机制(二)—— 使用Reveal查看任意APP的UI
3. APP安全机制(三)—— Base64加密
4. APP安全机制(四)—— MD5加密
5. APP安全机制(五)—— 对称加密
6. APP安全机制(六)—— 非对称加密
7. APP安全机制(七)—— SHA加密
8. APP安全机制(八)—— 偏好设置的加密存储
9. APP安全机制(九)—— 基本iOS安全之钥匙链和哈希(一)
10. APP安全机制(十)—— 基本iOS安全之钥匙链和哈希(二)
11. APP安全机制(十一)—— 密码工具:提高用户安全性和体验(一)
12. APP安全机制(十二)—— 密码工具:提高用户安全性和体验(二)
源码
1. Swift
首先看一下工程结构。
下面看一下sb中的内容
下面看一下代码
1. API.swift
import Foundation
// MARK: - return types
public enum APIResult {
case success
case failure(_ error: String?)
}
public enum MotivationalLotteryResult {
case success(_ motivation: String)
case failure
}
// MARK: - API access class
public class API {
// Update this URL definition
static let baseURL = URL(string: "https://[your-domain].com")
static let defaultsKey = "TOKEN-KEY"
static let defaults = UserDefaults.standard
public static var token: String? {
get {
return defaults.string(forKey: API.defaultsKey)
}
set {
defaults.set(newValue, forKey: API.defaultsKey)
}
}
// MARK: - Access functions
/// Register a new user and save the returned access token.
///
/// - Parameters:
/// - username: desired username
/// - password: desired password
/// - completion: closure that receives an `APIResult` after the call completes. This is **NOT** called on the main thread.
public static func register(_ username: String, password: String, completion: @escaping (APIResult) -> Void) {
struct RegisterData: Encodable {
let username: String
let password: String
}
guard let baseURL = baseURL else {
completion(.failure(nil))
return
}
let body = RegisterData(username: username, password: password)
let url = baseURL.appendingPathComponent("/api/user")
var registerRequest = URLRequest(url: url)
registerRequest.addValue("application/json", forHTTPHeaderField: "Content-Type")
registerRequest.httpMethod = "POST"
do {
registerRequest.httpBody = try JSONEncoder().encode(body)
} catch {
completion(.failure(nil))
return
}
URLSession.shared.dataTask(with: registerRequest) { data, response, _ in
DispatchQueue.main.async {
guard let jsonData = data else {
completion(.failure(nil))
return
}
guard
let httpResponse = response as? HTTPURLResponse,
200..<300 ~= httpResponse.statusCode else {
do {
let json = try JSONSerialization.jsonObject(with: jsonData, options: JSONSerialization.ReadingOptions.allowFragments) as? [String: Any]
if let error = json?["reason"] as? String {
completion(.failure(error))
return
}
} catch {}
completion(.failure(nil))
return
}
do {
let token = try JSONDecoder().decode(Token.self, from: jsonData)
self.token = token.token
completion(.success)
} catch {
completion(.failure(nil))
}
}
}
.resume()
}
/// Login an existing user and save the returned access token.
///
/// - Parameters:
/// - username: user's username
/// - password: user's password
/// - completion: closure that receives an `APIResult` after the call completes. This is **NOT** called on the main thread.
public static func login(_ username: String, password: String, completion: @escaping (APIResult) -> Void) {
guard let loginString = "\(username):\(password)"
.data(using: .utf8)?
.base64EncodedString()
else {
fatalError()
}
guard let baseURL = baseURL else {
completion(.failure(nil))
return
}
let url = baseURL.appendingPathComponent("/api/login")
var loginRequest = URLRequest(url: url)
loginRequest.addValue("Basic \(loginString)", forHTTPHeaderField: "Authorization")
loginRequest.httpMethod = "POST"
URLSession.shared.dataTask(with: loginRequest) { data, response, _ in
DispatchQueue.main.async {
guard let jsonData = data else {
completion(.failure(nil))
return
}
guard
let httpResponse = response as? HTTPURLResponse,
200..<300 ~= httpResponse.statusCode else {
do {
let json = try JSONSerialization.jsonObject(with: jsonData, options: JSONSerialization.ReadingOptions.allowFragments) as? [String: Any]
if let error = json?["reason"] as? String {
completion(.failure(error))
return
}
} catch {}
completion(.failure(nil))
return
}
do {
let token = try JSONDecoder().decode(Token.self, from: jsonData)
self.token = token.token
completion(.success)
} catch {
completion(.failure(nil))
}
}
}
.resume()
}
/// Log the user out and delete the saved access token.
public static func logout() {
guard let baseURL = baseURL, let token = token else {
return
}
let url = baseURL.appendingPathComponent("/api/logout")
var logoutRequest = URLRequest(url: url)
logoutRequest.addValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
logoutRequest.httpMethod = "POST"
URLSession.shared.dataTask(with: logoutRequest) { _, response, _ in
DispatchQueue.main.async {
guard
let httpResponse = response as? HTTPURLResponse,
200..<300 ~= httpResponse.statusCode
else {
return
}
self.token = nil
}
}
.resume()
}
/// Change the user's password
///
/// - Parameters:
/// - newPassword: the desired new password
/// - completion: closure that receives an `APIResult` after the call completes. This is **NOT** called on the main thread.
public static func changePassword(_ newPassword: String, completion: @escaping (APIResult) -> Void) {
struct NewPasswordData: Encodable {
let newPassword: String
}
guard let baseURL = baseURL, let token = token else {
completion(.failure(nil))
return
}
let body = NewPasswordData(newPassword: newPassword)
let url = baseURL.appendingPathComponent("/api/user")
var changeRequest = URLRequest(url: url)
changeRequest.addValue("application/json", forHTTPHeaderField: "Content-Type")
changeRequest.addValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
changeRequest.httpMethod = "PUT"
do {
changeRequest.httpBody = try JSONEncoder().encode(body)
} catch {
completion(.failure(nil))
return
}
URLSession.shared.dataTask(with: changeRequest) { _, response, _ in
DispatchQueue.main.async {
guard
let httpResponse = response as? HTTPURLResponse,
200..<300 ~= httpResponse.statusCode
else {
completion(.failure(nil))
return
}
completion(.success)
}
}
.resume()
}
/// Retreive a new motivational quote for the logged in user
///
/// - Parameter completion: closure that receives a `MotivationalLotteryResult` after the call completes. This is **NOT** called on the main thread.
public static func motivationalLottery(_ completion: @escaping (MotivationalLotteryResult) -> Void) {
struct MotivationalLotteryData: Decodable {
let motivation: String
}
guard let baseURL = baseURL, let token = token else {
completion(.failure)
return
}
let url = baseURL.appendingPathComponent("/api/motivation")
var motivationalLotteryRequest = URLRequest(url: url)
motivationalLotteryRequest.addValue("application/json", forHTTPHeaderField: "Content-Type")
motivationalLotteryRequest.addValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
URLSession.shared.dataTask(with: motivationalLotteryRequest) {
data, response, _ in
DispatchQueue.main.async {
guard
let httpResponse = response as? HTTPURLResponse,
200..<300 ~= httpResponse.statusCode,
let jsonData = data
else {
completion(.failure)
return
}
do {
let motivationalLottery = try JSONDecoder().decode(MotivationalLotteryData.self, from: jsonData)
completion(.success(motivationalLottery.motivation))
} catch {
completion(.failure)
}
}
}
.resume()
}
}
// MARK: - private structure
final class Token: Codable {
var token: String
var userID: UUID
init(token: String, userID: UUID) {
self.token = token
self.userID = userID
}
}
2. UltraMotivatorViewController.swift
import UIKit
class UltraMotivatorViewController: UIViewController {
var keyboardDismisser : UITapGestureRecognizer!
override func viewDidLoad() {
super.viewDidLoad()
keyboardDismisser = UITapGestureRecognizer(target: self, action:#selector(handleTap(_:)))
view.addGestureRecognizer(keyboardDismisser)
}
@objc func handleTap(_ recognizer: UITapGestureRecognizer) {
view.endEditing(true)
}
func fillInFieldsReminder() {
showAlert("Error", message: "Fill in all the fields, please.")
}
func showAlert(_ title: String, message: String? = nil, completion: (() -> Void)? = nil) {
let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
let OKAction = UIAlertAction(title: "OK", style: .default) { _ in completion?() }
alertController.addAction(OKAction)
present(alertController, animated: true)
}
}
3. LoginViewController.swift
import UIKit
class LoginViewController: UltraMotivatorViewController {
@IBOutlet var usernameField : UITextField!
@IBOutlet var passwordField : UITextField!
@IBOutlet var submitButton : UIButton!
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(true)
view.endEditing(true)
usernameField.text = nil
passwordField.text = nil
}
@IBAction private func signIn(_ sender: Any) {
view.endEditing(true)
guard
let username = usernameField.text,
let password = passwordField.text,
!username.isEmpty, !password.isEmpty
else {
fillInFieldsReminder()
return
}
submitButton.isEnabled = false
API.login(username, password: password) { result in
self.submitButton.isEnabled = true
switch result {
case .success:
self.performSegue(withIdentifier: "Logged In", sender: nil)
case .failure(let error):
self.showAlert("Error", message: error ?? "Login Failed")
}
}
}
}
4. SignupViewController.swift
import UIKit
class SignupViewController: UltraMotivatorViewController {
@IBOutlet var usernameField : UITextField!
@IBOutlet var passwordField : UITextField!
@IBOutlet var submitButton : UIButton!
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
usernameField.becomeFirstResponder()
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(true)
view.endEditing(true)
if API.token == nil {
usernameField.text = nil
passwordField.text = nil
} else {
API.logout()
}
}
@IBAction func signUp(_ sender: Any) {
view.endEditing(true)
guard
let username = usernameField.text,
let password = passwordField.text,
!username.isEmpty, !password.isEmpty
else {
fillInFieldsReminder()
return
}
submitButton.isEnabled = false
API.register(username, password: password) { result in
self.submitButton.isEnabled = true
switch result {
case .success:
self.showAlert("Signed Up!", message: "Log in to get motivated") {
self.navigationController?.popToRootViewController(animated: true)
}
case .failure(let error):
self.showAlert("Error", message: error ?? "Sign Up Failed")
}
}
}
}
5. OneTimeCodeViewController.swift
import UIKit
class OneTimeCodeViewController: UltraMotivatorViewController {
@IBOutlet weak var oneTimeCodeField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.setHidesBackButton(true, animated: false)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
oneTimeCodeField.textContentType = .oneTimeCode
oneTimeCodeField.becomeFirstResponder()
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(true)
view.endEditing(true)
oneTimeCodeField.text = nil
}
@IBAction func submitTapped(_ sender: Any) {
view.endEditing(true)
performSegue(withIdentifier: "Verified", sender: nil)
}
}
6. MotivationalViewController.swift
import UIKit
class MotivationalViewController: UIViewController {
@IBOutlet weak var motivationalLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.setHidesBackButton(true, animated: false)
}
override func viewWillAppear(_ animated: Bool) {
motivationalLabel.isHidden = true
API.motivationalLottery { result in
if case let .success(motivation) = result {
self.motivationalLabel.text = motivation
}
self.motivationalLabel.isHidden = false
}
}
@IBAction func logoutTapped(_ sender: Any) {
API.logout()
navigationController?.popToRootViewController(animated: true)
}
}
后记
本篇讲述了提高用户安全性和体验,感兴趣的给个赞或者关注~~~
网友评论