如何在 Swift 中发出 HTTP 请求 + 基本身份验证

Posted

技术标签:

【中文标题】如何在 Swift 中发出 HTTP 请求 + 基本身份验证【英文标题】:How to make an HTTP request + basic auth in Swift 【发布时间】:2014-08-14 07:12:53 【问题描述】:

我有一个带有基本身份验证的 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");
    
    

【问题讨论】:

顺便说一句,NSURLConnection(request: request, delegate: self) 将为您提供start 连接。不要自己显式调用start 方法,有效地再次启动它。 NSURLConnection 已弃用。你真的应该切换到 NSURLSession。 【参考方案1】:

您在 URLRequest 实例中提供凭据,例如在 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)

或者在 Swift 2 中使用 NSMutableURLRequest

// 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”,它适用于我很好 好收获!更新了答案。 'NSDataBase64EncodingOptions.Type' 没有名为 'fromMask' 的成员。这是我在 Xcode 6.1 中遇到的错误。请帮忙。什么是 mask(0) 我在 xCode 中也看到了与 @BalaVishnu 相同的消息,但我只是使用了 .allZeros Swift 的选项集语法在 Xcode 1.1 中发生了变化。您可以使用NSDataBase64EncodingOptions(0)nil 不作任何选择。更新了答案。【参考方案2】:

迅速 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")

【讨论】:

如果你改用Data(loginString.utf8),你就不需要保护声明了。【参考方案3】:

//创建认证base 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"

//您可以使用以下方法之一

//1 带有 NSURLConnectionDataDelegate 的 URL 请求

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

//2 带有异步请求的 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()

//如果你在服务器请求GET请求时更改了request.HTTPMethod = "POST",你可能会遇到致命错误

【讨论】:

顺便说一句,这重复了 OP 代码中的错误:NSURLConnection(request: request, delegate: self) 启动请求。你不应该再start它。【参考方案4】:

在 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 之前是否需要转义任何内容【参考方案5】:

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)



【讨论】:

【参考方案6】:

我在尝试将一些在应用程序中实现的自动电子邮件发布到 MailGun 时遇到了类似的问题。

我能够通过大型 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 请求中使用 3rd 方代码的人提供解决方案!

【讨论】:

【参考方案7】:

我的解决方案如下:

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 变量,并将您的按钮连接到带注释的 IBAction 函数。

说明: 在函数登录中,您使用 NSURL、NSURLRequest 和 NSURLConnection 创建您的请求。 这里必不可少的是引用此类(self)的委托。 要接收代表电话,您需要

将协议 NSURLConnectionDataDelegate 添加到类中 实现协议的功能“connection:willSendRequestForAuthenticationChallenge” 这用于将凭据添加到请求中 实现协议的功能“connection:didReceiveResponse” 这将检查http响应状态代码

【讨论】:

有没有办法检查同步请求的http响应状态码? NSURLConnection 已弃用。 Apple 强烈建议您使用 NSURLSession。【参考方案8】:

我在登录按钮点击时调用 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

【讨论】:

以上是关于如何在 Swift 中发出 HTTP 请求 + 基本身份验证的主要内容,如果未能解决你的问题,请参考以下文章

在 Swift 中使用 JSON 正文发出 HTTP Post 请求

如何在 Swift 中发出 NSURLSession POST 请求

如何在 Alamofire 请求 swift 3 上发出警报

如何调试网络请求 Swift

如何使 Http 获取请求 URL 由 swift 3 中的管道组成?

在 Swift 中保存 http 请求的响应