So stellen Sie eine HTTP-Anfrage + Basisauthentifizierung in Swift


92

Ich habe einen RESTFull-Dienst mit Basisauthentifizierung und möchte ihn von iOS + Swift aus aufrufen. Wie und wo muss ich für diese Anfrage einen Berechtigungsnachweis bereitstellen?

Mein Code (Entschuldigung, ich fange gerade an, iOS / obj-c / swift zu lernen):

class APIProxy: NSObject {
var data: NSMutableData = NSMutableData()

func connectToWebApi() {
    var urlPath = "http://xx.xx.xx.xx/BP3_0_32/ru/hs/testservis/somemethod"
    NSLog("connection string \(urlPath)")
    var url: NSURL = NSURL(string: urlPath)
    var request = NSMutableURLRequest(URL: url)
    let username = "hs"
    let password = "1"
    let loginString = NSString(format: "%@:%@", username, password)
    let loginData: NSData = loginString.dataUsingEncoding(NSUTF8StringEncoding)
    let base64LoginString = loginData.base64EncodedStringWithOptions(NSDataBase64EncodingOptions.fromMask(0))
    request.setValue(base64LoginString, forHTTPHeaderField: "Authorization")

    var connection: NSURLConnection = NSURLConnection(request: request, delegate: self)

    connection.start()
}


//NSURLConnection delegate method
func connection(connection: NSURLConnection!, didFailWithError error: NSError!) {
    println("Failed with error:\(error.localizedDescription)")
}

//NSURLConnection delegate method
func connection(didReceiveResponse: NSURLConnection!, didReceiveResponse response: NSURLResponse!) {
    //New request so we need to clear the data object
    self.data = NSMutableData()
}

//NSURLConnection delegate method
func connection(connection: NSURLConnection!, didReceiveData data: NSData!) {
    //Append incoming data
    self.data.appendData(data)
}

//NSURLConnection delegate method
func connectionDidFinishLoading(connection: NSURLConnection!) {
    NSLog("connectionDidFinishLoading");
}

}}


Übrigens, NSURLConnection(request: request, delegate: self)wird startdie Verbindung für Sie. Rufen Sie die startMethode nicht explizit selbst auf und starten Sie sie effektiv ein zweites Mal.
Rob

3
NSURLConnection ist veraltet. Sie sollten wirklich zu NSURLSession wechseln.
Sam Soffes

Antworten:


165

Sie geben Anmeldeinformationen in einer URLRequestInstanz wie dieser in Swift 3 ein:

let username = "user"
let password = "pass"
let loginString = String(format: "%@:%@", username, password)
let loginData = loginString.data(using: String.Encoding.utf8)!
let base64LoginString = loginData.base64EncodedString()

// create the request
let url = URL(string: "http://www.example.com/")!
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("Basic \(base64LoginString)", forHTTPHeaderField: "Authorization")

// fire off the request
// make sure your class conforms to NSURLConnectionDelegate
let urlConnection = NSURLConnection(request: request, delegate: self)

Oder in einem NSMutableURLRequestin Swift 2:

// set up the base64-encoded credentials
let username = "user"
let password = "pass"
let loginString = NSString(format: "%@:%@", username, password)
let loginData: NSData = loginString.dataUsingEncoding(NSUTF8StringEncoding)!
let base64LoginString = loginData.base64EncodedStringWithOptions([])

// create the request
let url = NSURL(string: "http://www.example.com/")
let request = NSMutableURLRequest(URL: url)
request.HTTPMethod = "POST"
request.setValue("Basic \(base64LoginString)", forHTTPHeaderField: "Authorization")

// fire off the request
// make sure your class conforms to NSURLConnectionDelegate
let urlConnection = NSURLConnection(request: request, delegate: self)

request.setValue (base64LoginString, forHTTPHeaderField: "Authorization") => request.setValue ("Basic (base64LoginString)", forHTTPHeaderField: "Authorization") Ich habe das Wort "Basic" hinzugefügt und es funktioniert gut für mich
MrKos

1
Guter Fang! Die Antwort wurde aktualisiert.
Nate Cook

4
‚NSDataBase64EncodingOptions.Type‘ hat kein Mitglied namens ‚fromMask‘ .. Dies ist der Fehler , den ich in Xcode 6.1..Pls help..What erhalten wird Maske (0)
Bala Vishnu

2
Ich sehe auch die gleiche Nachricht wie @BalaVishnu in xCode, aber ich habe stattdessen nur .allZeros verwendet
Sean Larkin

1
Die Swift-Syntax für Optionssätze wurde in Xcode 1.1 geändert. Sie können NSDataBase64EncodingOptions(0)oder nilfür keine Optionen verwenden. Die Antwort wurde aktualisiert.
Nate Cook

21

// Authentifizierungsbasis 64-Codierungszeichenfolge erstellen

    let PasswordString = "\(txtUserName.text):\(txtPassword.text)"
    let PasswordData = PasswordString.dataUsingEncoding(NSUTF8StringEncoding)
    let base64EncodedCredential = PasswordData!.base64EncodedStringWithOptions(NSDataBase64EncodingOptions.Encoding64CharacterLineLength)
    //let base64EncodedCredential = PasswordData!.base64EncodedStringWithOptions(nil)

// Authentifizierungs-URL erstellen

    let urlPath: String = "http://...../auth"
    var url: NSURL = NSURL(string: urlPath)

// Basisauthentifizierungsanforderung erstellen und initialisieren

    var request: NSMutableURLRequest = NSMutableURLRequest(URL: url)
    request.setValue("Basic \(base64EncodedCredential)", forHTTPHeaderField: "Authorization")
    request.HTTPMethod = "GET"

// Sie können eine der folgenden Methoden verwenden

// 1 URL-Anfrage mit NSURLConnectionDataDelegate

    let queue:NSOperationQueue = NSOperationQueue()
    let urlConnection = NSURLConnection(request: request, delegate: self)
    urlConnection.start()

// 2 URL-Anfrage mit AsynchronousRequest

    NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue.mainQueue()) {(response, data, error) in
        println(NSString(data: data, encoding: NSUTF8StringEncoding))
    }

// 2 URL-Anfrage mit AsynchronousRequest mit JSON-Ausgabe

    NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue.mainQueue(), completionHandler:{ (response: NSURLResponse!, data: NSData!, error: NSError!) -> Void in
        var err: NSError
        var jsonResult: NSDictionary = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: nil) as NSDictionary
        println("\(jsonResult)")
    })

// 3 URL-Anfrage mit SynchronousRequest

    var response: AutoreleasingUnsafePointer<NSURLResponse?>=nil
    var dataVal: NSData =  NSURLConnection.sendSynchronousRequest(request, returningResponse: response, error:nil)
    var err: NSError
    var jsonResult: NSDictionary = NSJSONSerialization.JSONObjectWithData(dataVal, options: NSJSONReadingOptions.MutableContainers, error: nil) as NSDictionary
    println("\(jsonResult)")

// 4 URL-Anfrage mit NSURLSession

    let config = NSURLSessionConfiguration.defaultSessionConfiguration()
    let authString = "Basic \(base64EncodedCredential)"
    config.HTTPAdditionalHeaders = ["Authorization" : authString]
    let session = NSURLSession(configuration: config)

    session.dataTaskWithURL(url) {
        (let data, let response, let error) in
        if let httpResponse = response as? NSHTTPURLResponse {
            let dataString = NSString(data: data, encoding: NSUTF8StringEncoding)
            println(dataString)
        }
    }.resume()

// Möglicherweise wird ein schwerwiegender Fehler angezeigt, wenn Sie die Anforderung geändert haben. HTTPMethod = "POST", wenn die Serveranforderung die GET-Anforderung anfordert


2
Übrigens wiederholt dies den Fehler im OP-Code: NSURLConnection(request: request, delegate: self)Startet die Anforderung. Sie sollten es nicht startein zweites Mal.
Rob

18

schnell 4:

let username = "username"
let password = "password"
let loginString = "\(username):\(password)"

guard let loginData = loginString.data(using: String.Encoding.utf8) else {
    return
}
let base64LoginString = loginData.base64EncodedString()

request.httpMethod = "GET"
request.setValue("Basic \(base64LoginString)", forHTTPHeaderField: "Authorization")

6

In Swift 2:

extension NSMutableURLRequest {
    func setAuthorizationHeader(username username: String, password: String) -> Bool {
        guard let data = "\(username):\(password)".dataUsingEncoding(NSUTF8StringEncoding) else { return false }

        let base64 = data.base64EncodedStringWithOptions([])
        setValue("Basic \(base64)", forHTTPHeaderField: "Authorization")
        return true
    }
}

Ich bin nicht sicher, ob Sie etwas entkommen müssen, bevor Sie es in base64 konvertieren
Sam Soffes

3

Gehen Sie einfach für SWIFT 3 und APACHE simple Auth:

func urlSession(_ session: URLSession, task: URLSessionTask,
                didReceive challenge: URLAuthenticationChallenge,
                completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {

    let credential = URLCredential(user: "test",
                                   password: "test",
                                   persistence: .none)

    completionHandler(.useCredential, credential)


}

2

Ich hatte ein ähnliches Problem beim Versuch, für einige automatisierte E-Mails, die ich in einer App implementierte, einen Post an MailGun zu senden.

Ich konnte dies mit einer großen HTTP-Antwort zum Laufen bringen. Ich habe den vollständigen Pfad in Keys.plist eingefügt, damit ich meinen Code auf github hochladen und einige der Argumente in Variablen aufteilen kann, damit ich sie später programmgesteuert festlegen kann.

// Email the FBO with desired information
// Parse our Keys.plist so we can use our path
var keys: NSDictionary?

if let path = NSBundle.mainBundle().pathForResource("Keys", ofType: "plist") {
    keys = NSDictionary(contentsOfFile: path)
}

if let dict = keys {
    // variablize our https path with API key, recipient and message text
    let mailgunAPIPath = dict["mailgunAPIPath"] as? String
    let emailRecipient = "bar@foo.com"
    let emailMessage = "Testing%20email%20sender%20variables"

    // Create a session and fill it with our request
    let session = NSURLSession.sharedSession()
    let request = NSMutableURLRequest(URL: NSURL(string: mailgunAPIPath! + "from=FBOGo%20Reservation%20%3Cscheduler@<my domain>.com%3E&to=reservations@<my domain>.com&to=\(emailRecipient)&subject=A%20New%20Reservation%21&text=\(emailMessage)")!)

    // POST and report back with any errors and response codes
    request.HTTPMethod = "POST"
    let task = session.dataTaskWithRequest(request, completionHandler: {(data, response, error) in
        if let error = error {
            print(error)
        }

        if let response = response {
            print("url = \(response.URL!)")
            print("response = \(response)")
            let httpResponse = response as! NSHTTPURLResponse
            print("response code = \(httpResponse.statusCode)")
        }
    })
    task.resume()
}

Der Mailgun-Pfad befindet sich in Keys.plist als Zeichenfolge mit dem Namen mailgunAPIPath mit dem Wert:

https://API:key-<my key>@api.mailgun.net/v3/<my domain>.com/messages?

Ich hoffe, dies bietet eine Lösung für jemanden, der versucht, die Verwendung von Code von Drittanbietern für seine POST-Anfragen zu vermeiden!


1

Meine Lösung funktioniert wie folgt:

import UIKit


class LoginViewController: UIViewController, NSURLConnectionDataDelegate {

  @IBOutlet var usernameTextField: UITextField
  @IBOutlet var passwordTextField: UITextField

  @IBAction func login(sender: AnyObject) {
    var url = NSURL(string: "YOUR_URL")
    var request = NSURLRequest(URL: url)
    var connection = NSURLConnection(request: request, delegate: self, startImmediately: true)

  }

  func connection(connection:NSURLConnection!, willSendRequestForAuthenticationChallenge challenge:NSURLAuthenticationChallenge!) {

    if challenge.previousFailureCount > 1 {

    } else {
        let creds = NSURLCredential(user: usernameTextField.text, password: passwordTextField.text, persistence: NSURLCredentialPersistence.None)
        challenge.sender.useCredential(creds, forAuthenticationChallenge: challenge)

    }

}

  func connection(connection:NSURLConnection!, didReceiveResponse response: NSURLResponse) {
    let status = (response as NSHTTPURLResponse).statusCode
    println("status code is \(status)")
    // 200? Yeah authentication was successful
  }


  override func viewDidLoad() {
    super.viewDidLoad()

  }

  override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()

  }  
}

Sie können diese Klasse als Implementierung eines ViewControllers verwenden. Verbinden Sie Ihre Felder mit den mit IBOutlet-Anmerkungen versehenen Variablen und Ihre Schaltfläche mit der mit IBAction-Anmerkungen versehenen Funktion.

Erläuterung: In der Funktionsanmeldung erstellen Sie Ihre Anfrage mit NSURL, NSURLRequest und NSURLConnection. Wesentlich ist hier der Delegat, der auf diese Klasse (Selbst) verweist. Für den Empfang der Delegiertenanrufe müssen Sie

  • Fügen Sie der Klasse das Protokoll NSURLConnectionDataDelegate hinzu
  • Implementieren Sie die Protokollfunktion "connection: willSendRequestForAuthenticationChallenge". Hiermit werden die Anmeldeinformationen zur Anforderung hinzugefügt
  • Implementieren Sie die Protokollfunktion "connection: didReceiveResponse". Dadurch wird der Statuscode der http-Antwort überprüft

Gibt es eine Möglichkeit, den http-Antwortstatuscode auf eine synchrone Anforderung zu überprüfen?
Matt

NSURLConnection ist veraltet. Apple empfiehlt Ihnen dringend, NSURLSession zu verwenden.
Sam Soffes

1

Ich rufe den json beim Anmelden an. Klicken Sie auf die Schaltfläche

@IBAction func loginClicked(sender : AnyObject){

var request = NSMutableURLRequest(URL: NSURL(string: kLoginURL)) // Here, kLogin contains the Login API.


var session = NSURLSession.sharedSession()

request.HTTPMethod = "POST"

var err: NSError?
request.HTTPBody = NSJSONSerialization.dataWithJSONObject(self.criteriaDic(), options: nil, error: &err) // This Line fills the web service with required parameters.
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")

var task = session.dataTaskWithRequest(request, completionHandler: {data, response, error -> Void in
 //   println("Response: \(response)")
var strData = NSString(data: data, encoding: NSUTF8StringEncoding)
println("Body: \(strData)")       
var err1: NSError?
var json2 = NSJSONSerialization.JSONObjectWithData(strData.dataUsingEncoding(NSUTF8StringEncoding), options: .MutableLeaves, error:&err1 ) as NSDictionary

println("json2 :\(json2)")

if(err) {
    println(err!.localizedDescription)
}
else {
    var success = json2["success"] as? Int
    println("Succes: \(success)")
}
})

task.resume()

}

Hier habe ich ein separates Wörterbuch für die Parameter erstellt.

var params = ["format":"json", "MobileType":"IOS","MIN":"f8d16d98ad12acdbbe1de647414495ec","UserName":emailTxtField.text,"PWD":passwordTxtField.text,"SigninVia":"SH"]as NSDictionary
     return params
}
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.