Comment faire une requête HTTP + authentification de base dans Swift

96

J'ai un service RESTFull avec authentification de base et je souhaite l'invoquer depuis iOS + swift. Comment et où dois-je fournir des informations d'identification pour cette demande?

Mon code (désolé, je viens de commencer à apprendre 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
la source
BTW, NSURLConnection(request: request, delegate: self)sera startla connexion pour vous. N'appelez pas la startméthode explicitement vous-même, en la démarrant effectivement une deuxième fois.
Rob
3
NSURLConnection est obsolète. Vous devriez vraiment passer à NSURLSession.
Sam Soffes

Réponses:

168

Vous fournissez des informations d'identification dans une URLRequestinstance, comme ceci dans 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)

Ou dans un 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
la source
request.setValue (base64LoginString, forHTTPHeaderField: "Authorization") => request.setValue ("Basic (base64LoginString)", forHTTPHeaderField: "Authorization") J'ai ajouté le mot "Basic" et cela fonctionne bien pour moi
MrKos
1
Bonne prise! Mise à jour de la réponse.
Nate Cook
4
'NSDataBase64EncodingOptions.Type' n'a pas de membre nommé 'fromMask' .. C'est l'erreur que j'obtiens dans Xcode 6.1..Pls help..What is mask (0)
Bala Vishnu
2
Je vois également le même message que @BalaVishnu dans xCode mais je viens d'utiliser .allZeros à la place
Sean Larkin
1
La syntaxe de Swift pour les jeux d'options a été modifiée dans Xcode 1.1. Vous pouvez utiliser NSDataBase64EncodingOptions(0)ou nilpour aucune option. Mise à jour de la réponse.
Nate Cook
22

// créer la chaîne de codage d'authentification base 64

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

// créer une URL d'authentification

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

// créer et initialiser la demande d'authentification de base

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

// Vous pouvez utiliser l'une des méthodes ci-dessous

// 1 requête URL avec NSURLConnectionDataDelegate

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

// 2 Requête URL avec AsynchronousRequest

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

// 2 Requête URL avec AsynchronousRequest avec sortie 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 Requête URL avec 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 Requête URL avec 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()

// vous pouvez obtenir une erreur fatale si vous avez modifié la requête.HTTPMethod = "POST" lorsque le serveur demande la requête GET

Subhash
la source
2
BTW, cela répète l'erreur dans le code de l'OP: NSURLConnection(request: request, delegate: self)lance la requête. Vous ne devriez pas le startfaire une seconde fois.
Rob
18

rapide 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
la source
6

Dans 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
la source
Je ne sais pas si vous devez échapper à quoi que ce soit avant de le convertir en base64
Sam Soffes
3

optez pour SWIFT 3 et 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
la source
2

J'ai eu un problème similaire en essayant de POSTER sur MailGun pour certains e-mails automatisés que j'implémentais dans une application.

J'ai pu faire fonctionner cela correctement avec une grande réponse HTTP. J'ai mis le chemin complet dans Keys.plist afin que je puisse télécharger mon code sur github et décomposer certains des arguments en variables afin que je puisse les avoir définis par programme plus tard.

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

Le chemin Mailgun est dans Keys.plist sous la forme d'une chaîne appelée mailgunAPIPath avec la valeur:

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

J'espère que cela aidera à offrir une solution à quelqu'un qui essaie d'éviter d'utiliser du code tiers pour ses demandes POST!

Bouddhiste le plus rudimentaire
la source
1

ma solution fonctionne comme suit:

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

  }  
}

Vous pouvez utiliser cette classe comme implémentation d'un ViewController. Connectez vos champs aux variables annotées IBOutlet et votre Button à la fonction annotée IBAction.

Explication: Dans la connexion de fonction, vous créez votre demande avec NSURL, NSURLRequest et NSURLConnection. Essentiel ici est le délégué qui fait référence à cette classe (self). Pour recevoir les appels des délégués, vous devez

  • Ajoutez le protocole NSURLConnectionDataDelegate à la classe
  • Implémentez la fonction des protocoles "connection: willSendRequestForAuthenticationChallenge" Ceci est utilisé pour ajouter les informations d'identification à la demande
  • Implémentez la fonction des protocoles "connection: didReceiveResponse" Cela vérifiera le code d'état de la réponse http
Oliver Koehler
la source
Existe-t-il un moyen de vérifier le code d'état de la réponse http pour une requête synchrone?
Matt
NSURLConnection est obsolète. Apple vous encourage vivement à utiliser NSURLSession.
Sam Soffes
1

J'appelle le json en cliquant sur le bouton de connexion

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

}

Ici, j'ai fait un dictionnaire séparé pour les paramètres.

var params = ["format":"json", "MobileType":"IOS","MIN":"f8d16d98ad12acdbbe1de647414495ec","UserName":emailTxtField.text,"PWD":passwordTxtField.text,"SigninVia":"SH"]as NSDictionary
     return params
}
Annu
la source