Swift에서 HTTP 요청 + 기본 인증을 만드는 방법


99

기본 인증이있는 RESTFull 서비스가 있으며 iOS + swift에서 호출하고 싶습니다. 이 요청에 대한 자격 증명을 어디에서 어떻게 제공해야합니까?

내 코드 (죄송합니다. 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");
}

}


BTW, 당신 NSURLConnection(request: request, delegate: self)start위해 연결됩니다. start메서드를 직접 명시 적으로 호출하지 말고 효과적으로 두 번째로 시작하십시오.
Rob

3
NSURLConnection은 더 이상 사용되지 않습니다. NSURLSession으로 전환해야합니다.
Sam Soffes

답변:


171

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

또는 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)

request.setValue (base64LoginString, forHTTPHeaderField : "Authorization") => request.setValue ( "Basic (base64LoginString)", forHTTPHeaderField : "Authorization") "Basic"이라는 단어를 추가
했으며

1
잘 잡아! 답변을 업데이트했습니다.
Nate Cook

4
'NSDataBase64EncodingOptions.Type'에 'fromMask'라는 멤버가 없습니다 .. 이것은 Xcode 6.1..pls help..What is mask (0)
Bala Vishnu

2
xCode에서 @BalaVishnu와 동일한 메시지가 표시되지만 대신 .allZeros를 사용했습니다
Sean Larkin

1
옵션 세트에 대한 Swift의 구문이 Xcode 1.1에서 변경되었습니다. 옵션을 사용 NSDataBase64EncodingOptions(0)하거나 사용 nil하지 않을 수 있습니다. 답변을 업데이트했습니다.
Nate Cook

22

// 인증 기반 64 인코딩 문자열 생성

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

// 인증 URL 생성

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

// 기본 인증 요청 생성 및 초기화

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

// 아래 방법 중 하나를 사용할 수 있습니다.

// NSURLConnectionDataDelegate를 사용한 URL 요청 1 개

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

// 2 AsynchronousRequest로 URL 요청

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

// 2 json 출력이있는 AsynchronousRequest가있는 URL 요청

    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 SynchronousRequest를 사용한 URL 요청

    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 NSURLSession을 사용한 URL 요청

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

// 요청을 변경하면 치명적 오류가 발생할 수 있습니다. HTTPMethod = "POST"서버 요청시 GET 요청


2
BTW, 이것은 OP 코드의 실수를 반복합니다 : NSURLConnection(request: request, delegate: self)요청을 시작합니다. start두 번째 는 안됩니다 .
Rob

19

신속한 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

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
    }
}

base64로 변환하기 전에 이스케이프가 필요한지 잘 모르겠습니다
Sam Soffes

4

SWIFT 3 및 APACHE 단순 인증의 경우 일반화 :

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

앱에서 구현하는 일부 자동화 된 이메일에 대해 MailGun에 POST를 시도하는 것과 유사한 문제가 발생했습니다.

큰 HTTP 응답으로 제대로 작동 할 수있었습니다. 전체 경로를 Keys.plist에 입력하여 코드를 github에 업로드하고 일부 인수를 변수로 분리하여 나중에 프로그래밍 방식으로 설정할 수 있습니다.

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

Mailgun 경로는 Keys.plist에 다음 값이있는 mailgunAPIPath라는 문자열로 있습니다.

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

이것이 POST 요청에 타사 코드를 사용하지 않으려는 사람에게 솔루션을 제공하는 데 도움이되기를 바랍니다!


1

내 솔루션은 다음과 같이 작동합니다.

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

  }  
}

이 클래스를 ViewController의 구현으로 사용할 수 있습니다. 필드를 IBOutlet 주석 변수에 연결하고 Button을 IBAction 주석 함수에 연결하십시오.

설명 : 로그인 기능에서 NSURL, NSURLRequest 및 NSURLConnection을 사용하여 요청을 작성합니다. 여기에 필수적인 것은이 클래스 (self)를 참조하는 델리게이트입니다. 대표 전화를 받으려면 다음을 수행해야합니다.

  • NSURLConnectionDataDelegate 프로토콜을 클래스에 추가하십시오.
  • 프로토콜의 기능 "connection : willSendRequestForAuthenticationChallenge"구현 요청에 자격 증명을 추가하는 데 사용됩니다.
  • 프로토콜의 기능 "connection : didReceiveResponse"구현 http 응답 상태 코드를 확인합니다.

동기 요청에 대한 http 응답 상태 코드를 확인하는 방법이 있습니까?
Matt

NSURLConnection은 더 이상 사용되지 않습니다. Apple은 NSURLSession을 사용할 것을 강력히 권장합니다.
샘 Soffes

1

로그인 버튼 클릭시 json을 호출하고 있습니다.

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

}

여기에서는 매개 변수에 대한 별도의 사전을 만들었습니다.

var params = ["format":"json", "MobileType":"IOS","MIN":"f8d16d98ad12acdbbe1de647414495ec","UserName":emailTxtField.text,"PWD":passwordTxtField.text,"SigninVia":"SH"]as NSDictionary
     return params
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.