zoukankan      html  css  js  c++  java
  • iOS 新浪微博-4.0 OAuth授权

    申请开发者

    想要拉到到新浪微博的数据,首先让自己成为开发者。申请成为开发者账号很简单,只要有新浪微博的账号即可。

    申请地址:http://open.weibo.com/

    在开发的过程中,我们需要拿到几下几个值:

    AppKey :分配给每个第三方应用的 app key。用于鉴权身份,显示来源等功能。

    AppSecret :分配给每个第三方应用的 app 私钥。

    RedirectURI:应用回调页面,可在新浪微博开放平台->我的应用->应用信息->高级应用->授权设置->应用回调页中找到。

    为了方便测试,除了可以使用当前开发者账户外,还可以自己添加15个关联测试账号。

    开发实现

    授权相关文档:http://open.weibo.com/wiki/%E6%8E%88%E6%9D%83%E6%9C%BA%E5%88%B6

    实现思路:

    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

  • 相关阅读:
    P3146 [USACO16OPEN]248
    P2590 [ZJOI2008]树的统计
    P3379 【模板】最近公共祖先(LCA)
    P2253 好一个一中腰鼓!
    数组中出现次数超过一半的数字
    字符串的排列
    二叉搜索树与双向链表
    二叉搜索树的后序遍历序列
    从上往下打印二叉树
    顺时针打印矩阵
  • 原文地址:https://www.cnblogs.com/jys509/p/4881144.html
Copyright © 2011-2022 走看看