Jak wykonać żądanie HTTP + podstawowe uwierzytelnianie w Swift

99

Mam usługę RESTFull z podstawowym uwierzytelnianiem i chcę ją wywołać z iOS + swift. Jak i gdzie muszę podać poświadczenia dla tego żądania?

Mój kod (przepraszam, dopiero zaczynam się uczyć iOS / obj-c / swift):

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");
}

}

MrKos
źródło
BTW, NSURLConnection(request: request, delegate: self)będzie startpołączenie dla Ciebie. Nie wywołuj startmetody jawnie samodzielnie, skutecznie uruchamiając ją po raz drugi.
Rob
3
NSURLConnection jest przestarzałe. Naprawdę powinieneś przełączyć się na NSURLSession.
Sam Soffes

Odpowiedzi:

171

Podajesz poświadczenia w URLRequestinstancji, na przykład w Swift 3:

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)

Lub w NSMutableURLRequestSwift 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)
Nate Cook
źródło
request.setValue (base64LoginString, forHTTPHeaderField: "Authorization") => request.setValue ("Basic (base64LoginString)", forHTTPHeaderField: "Authorization") Dodałem słowo "Basic" i działa dobrze
MrKos
1
Dobry chwyt! Zaktualizowałem odpowiedź.
Nate Cook
4
„NSDataBase64EncodingOptions.Type” nie ma elementu członkowskiego o nazwie „fromMask” .. To jest błąd, który dostaję w Xcode 6.1 .. Pomoc Pls .. Co to jest maska ​​(0)
Bala Vishnu
2
Widzę również tę samą wiadomość co @BalaVishnu w xCode, ale zamiast tego użyłem .allZeros
Sean Larkin
1
Składnia Swift dla zestawów opcji zmieniona w Xcode 1.1. Możesz użyć NSDataBase64EncodingOptions(0)lub nilbez opcji. Zaktualizowałem odpowiedź.
Nate Cook
22

// utwórz ciąg kodowania uwierzytelniania bazowego 64

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

// utwórz adres URL uwierzytelniania

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

// tworzy i inicjuje podstawowe żądanie uwierzytelnienia

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

// Możesz użyć jednej z poniższych metod

// 1 żądanie adresu URL z NSURLConnectionDataDelegate

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

// 2 Żądanie adresu URL z AsynchronousRequest

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

// 2 Żądanie adresu URL z AsynchronousRequest z wyjściem JSON

    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 żądanie adresu URL z 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 Żądanie adresu URL z sesją NSURL

    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()

// możesz otrzymać błąd krytyczny, jeśli zmienisz żądanie.HTTPMethod = "POST", gdy serwer zażąda żądania GET

Subhash
źródło
2
BTW, to powtarza błąd w kodzie OP: NSURLConnection(request: request, delegate: self)uruchamia żądanie. Nie powinieneś starttego robić po raz drugi.
Rob
19

szybki 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")
Amr
źródło
6

W 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
    }
}
Sam Soffes
źródło
Nie jestem pewien, czy musisz przed czymkolwiek uciec przed przekonwertowaniem go na base64
Sam Soffes,
4

proste dla SWIFT 3 i 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)


}
ingconti
źródło
2

Miałem podobny problem podczas próby wysłania POST do MailGun dla niektórych automatycznych e-maili, które implementowałem w aplikacji.

Udało mi się to poprawnie działać z dużą odpowiedzią HTTP. Umieściłem pełną ścieżkę w Keys.plist, aby móc przesłać swój kod na github i podzielić niektóre argumenty na zmienne, aby móc je programowo ustawić później.

// 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 = "[email protected]"
    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()
}

Ścieżka Mailgun znajduje się w Keys.plist jako ciąg o nazwie mailgunAPIPath i ma wartość:

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

Mam nadzieję, że pomoże to zaoferować rozwiązanie dla kogoś, kto próbuje uniknąć używania kodu innej firmy w swoich żądaniach POST!

Najgłupszy buddysta
źródło
1

moje rozwiązanie działa w następujący sposób:

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()

  }  
}

Możesz użyć tej klasy jako implementacji ViewController. Połącz swoje pola ze zmiennymi z adnotacjami IBOutlet, a Twój Button z funkcją z adnotacjami IBAction.

Objaśnienie: W funkcji logowania tworzysz żądanie za pomocą nazw NSURL, NSURLRequest i NSURLConnection. Istotny jest tutaj delegat, który odwołuje się do tej klasy (self). Aby odbierać telefony od delegatów, musisz

  • Dodaj protokół NSURLConnectionDataDelegate do klasy
  • Zaimplementuj funkcję protokołów „connection: willSendRequestForAuthenticationChallenge” Służy do dodawania poświadczeń do żądania
  • Zaimplementuj funkcję protokołów „connection: didReceiveResponse” Spowoduje to sprawdzenie kodu statusu odpowiedzi http
Oliver Koehler
źródło
Czy istnieje sposób sprawdzenia kodu stanu odpowiedzi HTTP dla żądania synchronicznego?
Matt,
NSURLConnection jest przestarzałe. Firma Apple zdecydowanie zachęca do korzystania z NSURLSession.
Sam Soffes
1

Wzywam json po kliknięciu przycisku logowania

@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()

}

Tutaj stworzyłem osobny słownik dla parametrów.

var params = ["format":"json", "MobileType":"IOS","MIN":"f8d16d98ad12acdbbe1de647414495ec","UserName":emailTxtField.text,"PWD":passwordTxtField.text,"SigninVia":"SH"]as NSDictionary
     return params
}
Annu
źródło