Swift 的 新浪微博 OAuth 认证实现

网络请求

Alamofire + SwiftyJSON

Alamofire
SwiftyJSON

封装网络请求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
import UIKit
import Alamofire
import SwiftyJSON

class HTTPTool{

static let shared = HTTPTool()

}


extension HTTPTool {

/// 网络请求方法
///
/// - Parameters:
/// - url: url
/// - method: method
/// - params: params
/// - success: 请求成功: 返回SwiftyJSON对象(可直接取子对象)
/// - errors: errors
func requestWithParameters(url: String, method: HTTPMethod, parameters params: Parameters, success: @escaping (_ json: JSON)->Void, errors: @escaping ()->Void) {

Alamofire.request(url, method: method, parameters: params).responseJSON { (response) in
switch response.result {

case .success:
if let value = response.result.value{
let json = JSON(value)

guard json["_error"].string == nil else{
errors()
return
}
success(json)
}
break

case .failure(let error):
NSLog("error,failure: \(error)")
errors()
break
}
}
}
}

OAuth认证

WebView加载授权网页

OAuth2的authorize接口

获取URL

定义 client_id 和 redirect_uri 为常量

1
2
fileprivate let client_id = ""
fileprivate let redirect_uri = ""

拼接URL

1
2
3
var oAuthURL:String {
return "https://api.weibo.com/oauth2/authorize?client_id=\(client_id)&redirect_uri=\(redirect_uri)&display=mobile&forcelogin=true"
}

加载授权网页

1
webView.loadRequest(request)

截取code

WebView代理

遵守协议并实现 shouldStartLoad 方法

1
2
3
4
5
6
extension WBOAuthViewController: UIWebViewDelegate{

func webView(_ webView: UIWebView, shouldStartLoadWith request: URLRequest, navigationType: UIWebViewNavigationType) -> Bool {

}
}

截取code字段

code是跟随返回地址的参数

实现 shouldStartLoad 方法后可以得到所有网络请求 , 若请求包含字段 error_uri , 则授权失败 , 若请求字段包含 code , 则授权成功 , 得到正确的 code

是否包含字段

1
let urlString = request.url?.query, urlString.hasPrefix("error_uri or code")

截取code字段

1
let code = urlString.substring(from: "code=".endIndex)

处理AccessToken

请求token

5个必选参数

获取code之后立即发起token的网络请求 , 请求成功后保存到模型中 , 由于网络工具类直接返回 SwiftyJSON 对象 , 则可以通过 ObjectMapper (ObjectMapper) 转为模型对象

返回数据为字典,调用 json.dictionaryObject 方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/// 请求Token信息
///
/// - Parameters:
/// - code: code description
/// - callBack: callBack description
func requestAccessToken(code: String, callBack: @escaping (WBUserInfo?) -> ()){

let urlString = "https://api.weibo.com/oauth2/access_token"

let paramenters = [
"client_id": client_id,
"client_secret": client_secret,
"grant_type": "authorization_code",
"code": code,
"redirect_uri": redirect_uri
]

HTTPTool.shared.requestWithParameters(url: urlString, method: .post, parameters: paramenters, success: { (json) in
let userInfo = Mapper<WBUserInfo>().map(JSONObject: json.dictionaryObject)
callBack(userInfo)
}) {

}

}

token本地化

模型转字典

1
var userData = userInfo.toJSON()

存储

1
2
3
4
func saveUserDefault(dict:[String: Any]) {
userDict = dict
UserDefaults.standard.set(dict, forKey: userInfoKey)
}

读取

1
2
3
4
5
6
func loadUserDefault() -> [String: Any]? {
if let data = UserDefaults.standard.dictionary(forKey: userInfoKey){
return data
}
return nil
}

根据token存储状态判断登录

1
2
3
4
5
6
7
func isLogin() -> Bool {
if let userDict = loadUserDefault() {
let isToken = userDict["access_token"] != nil
return isToken
}
return false
}