2016-07-04 75 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