iOS 新浪微博-4.0 OAuth受權

申請開發者

想要拉到到新浪微博的數據,首先讓本身成爲開發者。申請成爲開發者帳號很簡單,只要有新浪微博的帳號便可。javascript

申請地址:http://open.weibo.com/html

在開發的過程當中,咱們須要拿到幾下幾個值:java

AppKey :分配給每一個第三方應用的 app key。用於鑑權身份,顯示來源等功能。ios

AppSecret :分配給每一個第三方應用的 app 私鑰。git

RedirectURI:應用回調頁面,可在新浪微博開放平臺->個人應用->應用信息->高級應用->受權設置->應用回調頁中找到。github

爲了方便測試,除了可使用當前開發者帳戶外,還能夠本身添加15個關聯測試帳號。web

開發實現

受權相關文檔:http://open.weibo.com/wiki/%E6%8E%88%E6%9D%83%E6%9C%BA%E5%88%B6json

實現思路:api

  1. 拼接參數,請求OAuth2/authorize ,拿到受權Code值。
  2. 經過受權code值及相關申請的參數,拿到用戶受權UID及過時時間 
  3. 將返回的用戶信息存入沙盒裏
  4. 下次打開應用時,先從沙盒裏獲取,獲取爲nil時再請求網絡

導入相關第三方庫

直接在cocoapods裏導入如下的類庫網絡

  • 網絡請求:AFNetworking
  • 吐絲:MBProgressHUD
platform :ios,'7.0'
pod "AFNetworking","2.5.4"
pod 'MBProgressHUD', '~> 0.9.1'

因爲在不少地方都會使用到AFNetworking,爲了避免讓該第三方庫類「污染」,特地給AFNetworking封裝一層,方便之後切換第三方網絡請求類,同時也方便引用管理。

HttpTool.h

#import <Foundation/Foundation.h>

@interface HttpTool : NSObject

+ (void)get:(NSString *)url params:(NSDictionary *)params success:(void (^)(id json))success failure:(void (^)(NSError *error))failure;
+ (void)post:(NSString *)url params:(NSDictionary *)params success:(void (^)(id json))success failure:(void (^)(NSError *error))failure;

@end

HttpTool.m

#import "HttpTool.h"
#import "AFNetworking.h"

@implementation HttpTool

+ (void)get:(NSString *)url params:(NSDictionary *)params success:(void (^)(id json))success failure:(void (^)(NSError *error))failure
{
    // 1.建立請求管理者
    AFHTTPRequestOperationManager *mgr = [AFHTTPRequestOperationManager manager];
    
    mgr.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:@"application/json", @"text/html", @"text/json", @"text/javascript",@"text/plain", nil];
    
    // 2.發送請求
    [mgr GET:url parameters:params success:^(AFHTTPRequestOperation *operation, id responseObject) {
        if (success) {
            success(responseObject);
        }
    } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
        if (failure) {
            failure(error);
        }
    }];
}

+ (void)post:(NSString *)url params:(NSDictionary *)params success:(void (^)(id json))success failure:(void (^)(NSError *error))failure
{
    // 1.建立請求管理者
    AFHTTPRequestOperationManager *mgr = [AFHTTPRequestOperationManager manager];
    
    mgr.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:@"application/json", @"text/html", @"text/json", @"text/javascript",@"text/plain", nil];
    
    // 2.發送請求
    [mgr POST:url parameters:params success:^(AFHTTPRequestOperation *operation, id responseObject) {
        if (success) {
            success(responseObject);
        }
    } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
        if (failure) {
            failure(error);
        }
    }];
}

@end

定義帳戶常量

申請下來的帳號信息,咱們使用經常使用來存儲,固然,一些經常使用的不變的通用字符串,咱們也統一放在常量裏,須要避免使用宏。能夠參考:http://www.jianshu.com/p/f83335e036b5

Const.h

#import <Foundation/Foundation.h>

extern NSString * const AppKey;
extern NSString * const RedirectURI;
extern NSString * const AppSecret;

Const.m

#import "Const.h"

// 帳號信息
NSString * const AppKey = @"762197719";
NSString * const RedirectURI = @"http://www.baidu.com";
NSString * const AppSecret = @"89d739c387e3a69aaad0270da66c02ff";

歸檔access_token工具類

因爲access_token不常常變,咱們須要將拿到的access_token歸檔存起來,每次登陸的時候,先判斷access_token是否有效(存在且不過時)。

歸檔使用模型,須要實現NSCoding協議,定義模型

Account.h

#import <Foundation/Foundation.h>

@interface Account : NSObject<NSCoding>

/** string    用於調用access_token,接口獲取受權後的access token。*/
@property (nonatomic, copy) NSString *access_token;

/** string    access_token的生命週期,單位是秒數。*/
@property (nonatomic, copy) NSNumber *expires_in;

/** string    當前受權用戶的UID。*/
@property (nonatomic, copy) NSString *uid;

/**    access token的建立時間 */
@property (nonatomic, strong) NSDate *created_time;

/** 用戶的暱稱 */
@property (nonatomic, copy) NSString *name;

+ (instancetype)accountWithDict:(NSDictionary *)dict;

@end

Account.m

#import "Account.h"

@implementation Account

+(instancetype)accountWithDict:(NSDictionary *)dict
{
    Account *account = [[self alloc] init];
    account.access_token = dict[@"access_token"];
    account.uid = dict[@"uid"];
    account.expires_in = dict[@"expires_in"];
    // 得到帳號存儲的時間(accessToken的產生時間)
    account.created_time = [NSDate date];
    return account;
}

/**
 *  當一個對象要歸檔進沙盒中時,就會調用這個方法
 *  目的:在這個方法中說明這個對象的哪些屬性要存進沙盒
 */
- (void)encodeWithCoder:(NSCoder *)encoder
{
    [encoder encodeObject:self.access_token forKey:@"access_token"];
    [encoder encodeObject:self.expires_in forKey:@"expires_in"];
    [encoder encodeObject:self.uid forKey:@"uid"];
    [encoder encodeObject:self.created_time forKey:@"created_time"];
    [encoder encodeObject:self.name forKey:@"name"];
}

/**
 *  當從沙盒中解檔一個對象時(從沙盒中加載一個對象時),就會調用這個方法
 *  目的:在這個方法中說明沙盒中的屬性該怎麼解析(須要取出哪些屬性)
 */
- (id)initWithCoder:(NSCoder *)decoder
{
    if (self = [super init]) {
        self.access_token = [decoder decodeObjectForKey:@"access_token"];
        self.expires_in = [decoder decodeObjectForKey:@"expires_in"];
        self.uid = [decoder decodeObjectForKey:@"uid"];
        self.created_time = [decoder decodeObjectForKey:@"created_time"];
        self.name = [decoder decodeObjectForKey:@"name"];
    }
    return self;
}

@end

爲了方便直接歸檔和從歸檔裏讀取模型,定義一個AccountTool工具類

AccountTool.h

#import <Foundation/Foundation.h>
#import "Account.h"

@interface AccountTool : NSObject

/**
 *  存儲帳號信息
 *
 *  @param account 帳號模型
 */
+ (void)saveAccount:(Account *)account;

/**
 *  返回帳號信息
 *
 *  @return 帳號模型(若是帳號過時,返回nil)
 */
+ (Account *)getAccount;

@end

AccountTool.m

//
//  AccountTool.m
//  Weibo
//
//  Created by jiangys on 15/10/17.
//  Copyright © 2015年 Jiangys. All rights reserved.
//

#import "AccountTool.h"
#import "Account.h"

// 帳號的存儲路徑
#define AccountPath [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:@"account.archive"]

@implementation AccountTool

/**
 *  存儲帳號信息
 *
 *  @param account 帳號模型
 */
+ (void)saveAccount:(Account *)account
{
    // 自定義對象的存儲必須用NSKeyedArchiver,再也不有什麼writeToFile方法
    [NSKeyedArchiver archiveRootObject:account toFile:AccountPath];
}


/**
 *  返回帳號信息
 *
 *  @return 帳號模型(若是帳號過時,返回nil)
 */
+ (Account *)getAccount
{
    // 加載模型
    Account *account = [NSKeyedUnarchiver unarchiveObjectWithFile:AccountPath];
    
    /* 驗證帳號是否過時 */
    
    // 過時的秒數
    long long expires_in = [account.expires_in longLongValue];
    // 得到過時時間
    NSDate *expiresTime = [account.created_time dateByAddingTimeInterval:expires_in];
    // 得到當前時間
    NSDate *now = [NSDate date];
    
    // 若是expiresTime <= now,過時
    /**
     NSOrderedAscending = -1L, 升序,右邊 > 左邊
     NSOrderedSame, 同樣
     NSOrderedDescending 降序,右邊 < 左邊
     */
    NSComparisonResult result = [expiresTime compare:now];
    if (result != NSOrderedDescending) { // 過時
        return nil;
    }
    
    return account;
}

@end

獲取並存儲access_token

控制器須要調用authorize得到的code值,再經過code值獲取access_token

OAuthViewController.h

#import <UIKit/UIKit.h>

@interface OAuthViewController : UIViewController

@end

OAuthViewController.m

//
//  OAuthViewController.m
//  Weibo
//
//  Created by jiangys on 15/10/17.
//  Copyright © 2015年 Jiangys. All rights reserved.
//

#import "OAuthViewController.h"
#import "Const.h"
#import "HttpTool.h"
#import "MBProgressHUD+YS.h"
#import "Account.h"
#import "AccountTool.h"

@interface OAuthViewController() <UIWebViewDelegate>

@end

@implementation OAuthViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    // 1.建立一個webView
    UIWebView *webView=[[UIWebView alloc] init];
    webView.frame=self.view.bounds;
    webView.delegate=self;
    [self.view addSubview:webView];
    
    // 2.用webView加載新浪登陸頁面
    NSString *urlStr=[NSString stringWithFormat:@"https://api.weibo.com/oauth2/authorize?client_id=%@&redirect_uri=%@",AppKey,RedirectURI];
    NSURLRequest *requestUrl=[NSURLRequest requestWithURL:[NSURL URLWithString:urlStr]];
    [webView loadRequest:requestUrl];
}

#pragma mark - webView代理方法
-(void)webViewDidStartLoad:(UIWebView *)webView
{
    [MBProgressHUD showMessage:@"正在加載..."];
}

-(void)webViewDidFinishLoad:(UIWebView *)webView
{
    [MBProgressHUD hideHUD];
}

-(void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error
{
    [MBProgressHUD hideHUD];
}

/**
 *  監聽全部跳轉方法
 */
-(BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
    NSString *url=request.URL.absoluteString;
    NSRange range=[url rangeOfString:@"code="];
    if (range.length != 0) {
        // 截取code=後面的參數值
        NSUInteger fromIndex=NSMaxRange(range);
        NSString *code=[url substringFromIndex:fromIndex];
        
        // 利用code換取一個accessToken
        [self accessTokenWithCode:code];
        
        // 禁止加載回調地址
        return NO;
    }
    
    return true;
}

/**
 *  獲取token
 *
 *  @param code 受權碼
 */
- (void)accessTokenWithCode:(NSString *)code
{
    NSMutableDictionary *params=[NSMutableDictionary dictionary];
    params[@"client_id"] = AppKey;
    params[@"client_secret"] = AppSecret;
    params[@"grant_type"] = @"authorization_code";
    params[@"redirect_uri"] = RedirectURI;
    params[@"code"] = code;
    
    [HttpTool post:@"https://api.weibo.com/oauth2/access_token" params:params success:^(id json)
     {
         [MBProgressHUD hideHUD];
         
         // 將返回的帳號字典數據 --> 模型,存進沙盒
         Account *account = [Account accountWithDict:json];
         
         // 存儲帳號信息
         [AccountTool saveAccount:account];
         
         // 切換窗口的根控制器
         UIWindow *window = [UIApplication sharedApplication].keyWindow;
         [window switchRootViewController];
         
     }failure:^(NSError *error) {
         [MBProgressHUD hideHUD];
         YSLog(@"--MBProgressHUD_error--%@",error);
     }];
}


@end

登陸驗證access_token

每次登陸的時候,都須要判斷access_token是否有效(存在且不過時)

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

    self.window=[[UIWindow alloc]init];
    self.window.frame=[UIScreen mainScreen].bounds;
    
    // 2.設置根控制器
    Account *account = [AccountTool getAccount];
    if (account) { // 以前已經登陸成功過
        [self.window switchRootViewController];
    } else {
        self.window.rootViewController = [[OAuthViewController alloc] init];
    }
    
    [self.window makeKeyAndVisible];
    
    return YES;
}

章節源代碼下載:http://pan.baidu.com/s/1sjmoEw1

新浪微博Github:https://github.com/jiangys/Weibo

相關文章
相關標籤/搜索