2016-07-04 71 views
0

我正在研究訪問REST API Webservice的應用程序。一切工作都很好,除了我最近開始着手註銷和切換用戶的能力之外,我遇到了一個奇怪的情況。如果我註銷,然後再次單擊登錄,而不輸入它正在工作的密碼。我甚至調試了代碼,發現密碼是空的,但驗證仍在工作。下面是代碼:密碼正在緩存某處,需要清除密碼

import UIKit 
import LocalAuthentication 

var userName = String() 
var password = String() 
var server = String() 
var port = String() 
var myUser = User() 
var myExtensions = [ExtensionListItem]() 
var myDevices = [Device]() 

class LoginViewController: UIViewController, NSURLSessionDelegate, UITextFieldDelegate { 

let authContext: LAContext = LAContext() 

var logOutUser = Bool() 

@IBOutlet weak var activityIndicator: UIActivityIndicatorView! 
@IBOutlet weak var serverNameField: UITextField! 
@IBOutlet weak var usernameField: UITextField! 
@IBOutlet weak var passwordField: UITextField! 
@IBOutlet var loginEnable: UIButton! 
var userPasswordString = NSString() 
let userRequest = NSMutableURLRequest() 
var userSession = NSURLSession() 

override func viewDidLoad() { 
    super.viewDidLoad() 
    NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(logoff), name: "logoff", object: nil) 
    if logOutUser { 
     NSUserDefaults.standardUserDefaults().setValue("", forKey: "password") 
     NSURLCache.sharedURLCache().removeAllCachedResponses() 
     userPasswordString = NSString() 
    } 
    //Determine if the user has a stored Username and populate the usernameField if possible 
    if NSUserDefaults.standardUserDefaults().objectForKey("userName") != nil{ 
     usernameField.text = NSUserDefaults.standardUserDefaults().objectForKey("userName") as? String} 
    //Determine if the user has a stored ServerName and populate the serverNameField if possible. 
    if NSUserDefaults.standardUserDefaults().objectForKey("serverName") != nil{ 
     serverNameField.text = NSUserDefaults.standardUserDefaults().objectForKey("serverName") as? String} 
    //Determin if the user has requested to use Touch ID 
    if (NSUserDefaults.standardUserDefaults().objectForKey("useTouchID") != nil) { 
     if NSUserDefaults.standardUserDefaults().valueForKey("useTouchID") as! Bool == true && CheckTouchIDCapable(){ 
      //Trigger Touch ID 
      usernameField.enabled = false 
      passwordField.enabled = false 
      serverNameField.enabled = false 
      activityIndicator.startAnimating() 
      TouchIDCall() 
     } 
    } 
    // Do any additional setup after loading the view. 
} 

func logoff(){ 
    NSURLCache.sharedURLCache().removeAllCachedResponses() 
    userSession.invalidateAndCancel() 
} 



override func didReceiveMemoryWarning() { 
    super.didReceiveMemoryWarning() 
    NSURLCache.sharedURLCache().removeAllCachedResponses() 
    // Dispose of any resources that can be recreated. 
} 

@IBAction func loginButton(sender: AnyObject) { 

    NSUserDefaults.standardUserDefaults().setObject(usernameField.text, forKey: "userName") 
    NSUserDefaults.standardUserDefaults().setObject(passwordField.text, forKey: "password") 
    NSUserDefaults.standardUserDefaults().setObject(serverNameField.text, forKey: "serverName") 

    if NSUserDefaults.standardUserDefaults().valueForKey("touchIDPreferenceSet") == nil && CheckTouchIDCapable() { 
     DisplayTouchIDQuestion("Use Touch ID?", message: "Would you like to use touch ID to login?") 
    }else{ 
     usernameField.enabled = false 
     passwordField.enabled = false 
     serverNameField.enabled = false 
     activityIndicator.startAnimating() 
     CheckUser() 
    } 
    print("Password: \(password)") 
    print("Stored Password: \(NSUserDefaults.standardUserDefaults().valueForKey("password"))") 
    print("?? \(NSUserDefaults.standardUserDefaults().objectForKey("password"))") 

} 

func CheckUser(){ 


    userName = (NSUserDefaults.standardUserDefaults().objectForKey("userName") as? String)! 
    if !logOutUser{ 
     password = (NSUserDefaults.standardUserDefaults().objectForKey("password") as? String)! 
    } 
    server = (NSUserDefaults.standardUserDefaults().objectForKey("serverName") as? String)! 
    port = "8443" 
    // set up the base64-encoded credentials 
    let config = NSURLSessionConfiguration.defaultSessionConfiguration() 
    userPasswordString = NSString(format: "%@:%@", userName, password) 
    let userPasswordData = userPasswordString.dataUsingEncoding(NSUTF8StringEncoding) 
    let base64EncodedCredential = userPasswordData!.base64EncodedStringWithOptions(NSDataBase64EncodingOptions.Encoding64CharacterLineLength) 
    let authString = "Basic \(base64EncodedCredential)" 
    config.HTTPAdditionalHeaders?.removeAll() 
    config.HTTPAdditionalHeaders = ["Authorization" : authString] 
    config.timeoutIntervalForRequest = 10.0 


    // create the user request 
    let userUrlString = NSString(format: "https://%@:%@/webserver/user/%@", server, port, userName) 
    let userUrl = NSURL(string: userUrlString as String) 
    userRequest.cachePolicy = .ReloadIgnoringLocalAndRemoteCacheData 
    userRequest.URL = userUrl! 
    userRequest.HTTPMethod = "GET" 
    userRequest.setValue("Basic \(base64EncodedCredential)", forHTTPHeaderField: "Authorization") 
    userSession = NSURLSession(configuration: config, delegate: self, delegateQueue:NSOperationQueue.mainQueue()) 

    //Send User Request to the server and populate labels with response. 
    _ = userSession.dataTaskWithRequest(userRequest) { (data, response, error) in 
     dispatch_async(dispatch_get_main_queue(), {() -> Void in 

      if error?.code != nil{ 
       print("ERROR: \(error!.localizedDescription)") 
       self.DisplayAlert("Error", message: error!.localizedDescription) 
      }else{ 
       _ = NSString (data: data!, encoding: NSUTF8StringEncoding) 
       let dataString = NSString(data: data!, encoding: NSUTF8StringEncoding) 
       let accessDenied = Bool(dataString?.rangeOfString("HTTP Status 403").location != NSNotFound) 
       let authFailure = Bool(dataString?.rangeOfString("HTTP Status 401").location != NSNotFound) 

       if (authFailure || accessDenied) { 
        print("\(NSDate()): Unsuccessful Password Authentication Attempt for user: \(NSUserDefaults.standardUserDefaults().valueForKey("userName")!)") 
        self.DisplayAlert("Access Denied", message: "Please Verify Your Credentials") 
       }else{ 
        print("\(NSDate()): Successful Password Authentication for user: \(NSUserDefaults.standardUserDefaults().valueForKey("userName")!)") 
        self.performSegueWithIdentifier("authenticated", sender: self) 
       } 
      } 
     }) 
    }.resume() 
} 

func URLSession(session: NSURLSession, didReceiveChallenge challenge: NSURLAuthenticationChallenge, completionHandler: (NSURLSessionAuthChallengeDisposition, NSURLCredential?) -> Void) { 
    completionHandler(NSURLSessionAuthChallengeDisposition.UseCredential, NSURLCredential(forTrust: challenge.protectionSpace.serverTrust!)) 
} 

override func prefersStatusBarHidden() -> Bool { 
    return true 
} 

// MARK: - Keyboard Functions 

override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) { 
    self.view.endEditing(true) 
} 

func textFieldShouldReturn(textField: UITextField) -> Bool { 
    textField.resignFirstResponder() 
    if textField == passwordField && usernameField.text != "" && serverNameField.text != ""{ 
     loginButton(self) 
    } 
    return true 
} 

func ReEnableLogin(){ 

    self.activityIndicator.hidesWhenStopped = true 
    self.activityIndicator.stopAnimating() 
    self.usernameField.enabled = true 
    self.passwordField.enabled = true 
    self.serverNameField.enabled = true 
} 

func DisplayAlert(title: String, message: String){ 

    let alertController = UIAlertController(title: title, message: message, preferredStyle: UIAlertControllerStyle.Alert) 
    alertController.addAction(UIAlertAction(title: "Dismiss", style: UIAlertActionStyle.Default, handler: nil)) 
    self.presentViewController(alertController, animated: true, completion: nil) 
    self.ReEnableLogin() 

} 

func DisplayTouchIDQuestion(title: String, message: String){ 

    let alertControllerQuestion = UIAlertController(title: title, message: message, preferredStyle: UIAlertControllerStyle.Alert) 
    alertControllerQuestion.addAction(UIAlertAction(title: "Yes", style: UIAlertActionStyle.Default, handler: { (action:UIAlertAction) in 
     NSUserDefaults.standardUserDefaults().setValue(true, forKey: "useTouchID") 
     NSUserDefaults.standardUserDefaults().setValue(true, forKey: "touchIDPreferenceSet") 
     NSUserDefaults.standardUserDefaults().setValue(self.passwordField.text, forKey: "touchIDCachedCredential") 
     self.CheckUser() 
    })) 
    alertControllerQuestion.addAction(UIAlertAction(title: "No", style: UIAlertActionStyle.Default, handler: { (action:UIAlertAction) in 
     NSUserDefaults.standardUserDefaults().setValue(false, forKey: "useTouchID") 
     NSUserDefaults.standardUserDefaults().setValue(true, forKey: "touchIDPreferenceSet") 
     self.CheckUser() 
    })) 
    self.presentViewController(alertControllerQuestion, animated: true, completion: nil) 
} 

func CheckTouchIDCapable()-> Bool { 
    var error: NSError? 
    var touchEnabledDevice: Bool = false 
    if authContext.canEvaluatePolicy(LAPolicy.DeviceOwnerAuthenticationWithBiometrics, error: &error){ 
     touchEnabledDevice = true 
    } 
    return touchEnabledDevice 
} 

func TouchIDCall(){ 

    authContext.evaluatePolicy(LAPolicy.DeviceOwnerAuthenticationWithBiometrics, localizedReason: "Place your finger on the Home button to log into Collaboration User Tools", reply: { (wasSuccessful, error) in 
      if wasSuccessful{ 
       print("\(NSDate()): Successful Biometric Authentication for user: \(NSUserDefaults.standardUserDefaults().valueForKey("userName")!)") 
       NSUserDefaults.standardUserDefaults().setValue(NSUserDefaults.standardUserDefaults().valueForKey("touchIDCachedCredential"), forKey: "password") 
       self.CheckUser() 

      }else{ 
       print("\(NSDate()): Error: \(error!.code)") 
       print("\(NSDate()): Unsuccessful Biometric Authentication for user: \(NSUserDefaults.standardUserDefaults().valueForKey("userName")!)") 
       let qualityOfServiceClass = QOS_CLASS_USER_INTERACTIVE 
       let backgroundQueue = dispatch_get_global_queue(qualityOfServiceClass, 0) 
       dispatch_async(backgroundQueue, { 
        dispatch_async(dispatch_get_main_queue(), {() -> Void in 
         self.activityIndicator.stopAnimating() 
        }) 
       }) 
       self.ReEnableLogin() 
      } 
     }) 

} 





} 

我已經試過:

NSURLCache.sharedURLCache().removeAllCachedResponses() 
userSession.invalidatedAndCancel() 

註銷表視圖控制器調用此方法:

override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) { 
    switch indexPath.row{ 
    case 0: 
     myUser = User() 
     myExtensions.removeAll() 
     myDevices.removeAll() 
     NSUserDefaults.standardUserDefaults().setObject("", forKey: "password") 
     userName = "" 
     password = "" 
     NSURLCache.sharedURLCache().removeAllCachedResponses() 
     NSNotificationCenter.defaultCenter().postNotificationName("logoff", object: nil) 
     performSegueWithIdentifier("logout", sender: self) 
    default: 
     break 
    } 
} 

我不知道密碼是緩存。有任何想法嗎?

+0

你能不能展示整個代碼?從按鈕的Action插口到請求。 –

+0

完成。整個視圖控制器現在包括在內。謝謝! – GED125

+0

爲什麼不在註銷函數中調用NSUserDefaults.standardUserDefaults()。setValue(「」,forKey:「password」)? – Emptyless

回答

0

我的問題是從來沒有涉及到密碼緩存。最有可能是由於我的業餘經驗水平,我從來沒有考慮過這可能是COOKIES保持會議的事實。這正是它最終成爲的。我通過在註銷過程中刪除所有cookie來解決這種情況。我在註銷代碼中添加了以下內容,並且工作完美。

print("\(NSDate()): Logout Requested, Deleting Cookies") 
let cookieStorage = NSHTTPCookieStorage.sharedHTTPCookieStorage() 
let cookies = cookieStorage.cookies! as [NSHTTPCookie] 
print("\(NSDate()): Cookie count: \(cookies.count)") 
for cookie in cookies{ 
    print("\(NSDate()): Deleting Cookie name: \(cookie.name) value: \(cookie.value)") 
    NSHTTPCookieStorage.sharedHTTPCookieStorage().deleteCookie(cookie) 
} 
0

我認爲你的問題在於處理你的密碼時你的邏輯的一部分。首先,在您的func CheckUser()中,如果!logOutUser來自您的用戶默認值,則分配var password的值,但是此屬性(密碼)在任何時候都不會被清除,因此如果!logOutUserfalse(它看起來總是如此,因爲它沒有設置任何地方),並且您的輸入字段爲空,那麼它將使用您在那裏存儲的先前值。

因此,如果我是正確的,並且此處的罪魁禍首是密碼字段正在存儲其數據,則在執行請求後,快速骯髒的修復將只是清除這些字段(usernamepassword)。更好的解決方法是直接傳遞usernameField.text值並在完成後將其清除。 (我不確定爲什麼你在你的應用程序存儲用戶的密碼,我會建議反思,如果你需要它來驗證會話,你應該使用的認證令牌)

// set up the base64-encoded credentials 
let config = NSURLSessionConfiguration.defaultSessionConfiguration() 
userPasswordString = NSString(format: "%@:%@", userName, password) 
userName = "" 
password = "" 

我也想指出你應該爲這些字段使用可選項,你應該爲你的用戶默認使用可選的展開方式,並且還要添加一個驗證機制,這樣你就不會提交帶有空字段的請求,理想情況下你的按鈕將被禁用,或者你會提醒你的用戶的無效字段。

祝你好運!

編輯

試試這個辦法:而不是使用密碼,用戶名和服務器的值的類屬性。使用方法變量來設置你的值,這樣,當方法終止時,這些值將被丟棄。 (這是在黑暗中拍攝的,因爲我手頭沒有編譯器,所以可能會有一些小的語法錯誤)。 - 注意方法簽名

func CheckUser(username: String, password: String, server: String){ 
let port = "8443" 
let config = NSURLSessionConfiguration.defaultSessionConfiguration() 
let userPasswordString = NSString(format: "%@:%@", username, password) 
let userPasswordData = userPasswordString.dataUsingEncoding(NSUTF8StringEncoding) 
let base64EncodedCredential = userPasswordData!.base64EncodedStringWithOptions(NSDataBase64EncodingOptions.Encoding64CharacterLineLength) 
let authString = "Basic \(base64EncodedCredential)" 
config.HTTPAdditionalHeaders?.removeAll() 
config.HTTPAdditionalHeaders = ["Authorization" : authString] 
config.timeoutIntervalForRequest = 10.0 

// create the user request 
let userUrlString = NSString(format: "https://%@:%@/webserver/user/%@", server, port, username) 
let userUrl = NSURL(string: userUrlString as String) 
userRequest.cachePolicy = .ReloadIgnoringLocalAndRemoteCacheData 
userRequest.URL = userUrl! 
userRequest.HTTPMethod = "GET" 
userRequest.setValue("Basic \(base64EncodedCredential)", forHTTPHeaderField: "Authorization") 
userSession = NSURLSession(configuration: config, delegate: self, delegateQueue:NSOperationQueue.mainQueue()) 

//Send User Request to the server and populate labels with response. 
let task = userSession.dataTaskWithRequest(userRequest) { (data, response, error) in 
    dispatch_async(dispatch_get_main_queue(), {() -> Void in 

     if error?.code != nil{ 
      print("ERROR: \(error!.localizedDescription)") 
      self.DisplayAlert("Error", message: error!.localizedDescription) 
     }else{ 
      _ = NSString (data: data!, encoding: NSUTF8StringEncoding) 
      let dataString = NSString(data: data!, encoding: NSUTF8StringEncoding) 
      let accessDenied = Bool(dataString?.rangeOfString("HTTP Status 403").location != NSNotFound) 
      let authFailure = Bool(dataString?.rangeOfString("HTTP Status 401").location != NSNotFound) 

      if (authFailure || accessDenied) { 
       print("\(NSDate()): Unsuccessful Password Authentication Attempt for user: \(NSUserDefaults.standardUserDefaults().valueForKey("userName")!)") 
       self.DisplayAlert("Access Denied", message: "Please Verify Your Credentials") 
      }else{ 
       print("\(NSDate()): Successful Password Authentication for user: \(NSUserDefaults.standardUserDefaults().valueForKey("userName")!)") 
       self.performSegueWithIdentifier("authenticated", sender: self) 
      } 
     } 
    }) 
}.resume() 
} 

然後,你可以打電話給你的方法是這樣:

if NSUserDefaults.standardUserDefaults().valueForKey("touchIDPreferenceSet") == nil && CheckTouchIDCapable() { 
    DisplayTouchIDQuestion("Use Touch ID?", message: "Would you like to use touch ID to login?") 
}else{ 
    usernameField.enabled = false 
    passwordField.enabled = false 
    serverNameField.enabled = false 
    activityIndicator.startAnimating() 
    CheckUser(usernameField.text, password: passwordField.text, server: serverNameField.text) 
} 
+0

嗨丹尼爾,我有一個單獨的菜單,有註銷按鈕。我添加了在註銷期間執行的視圖控制器代碼。我正在清除所有內容,但仍在工作。 – GED125

+0

我明白了,您正在發佈通知以執行註銷方法,但在此方法中,您並未設置logOutUser。我會使用您的loginButton行動插座的替代解決方案更新我的答案。 –

+0

感謝您提供更新的解決方案,我會在早上檢查一下,即將慶祝第四天。還想指出,我現在正在prepareForSegue方法中設置「logOutUser」。我喜歡你的建議,我會給你一個機會,明天回覆你。 – GED125