年年有"余"

 找回密码
 立即注册

QQ登录

只需一步,快速开始

查看: 22005|回复: 55

[新浪微博] ios实战开发之仿新浪微博(第四讲:OAuth认证)

  [复制链接]
  • TA的每日心情
    奋斗
    2022-12-13 21:26
  • 签到天数: 371 天

    [LV.9]以坛为家II

    发表于 2014-4-16 00:22:27 | 显示全部楼层 |阅读模式
    1、效果演示



    2、设计思路
    2.1 创建一个OAuth认证控制器,并加载一个WebView
    2.2 WebView加载新浪认证的URL(注册新浪帐号并成为新浪开发者),参照 http://open.weibo.com
    2.3 使用WebView的代理方法发送RequestToken获取AccessToken
    2.3.1 通过返回的URL截取code值即为requestToken
    2.3.2 新建一个工具类封装第三方框架(AFNetWorking)的抓取网页数据方法获取JSON
    2.3.3 创建一个数据模型保存JSON并归档
    2.4 通过判断归档中是否存在AccessToken来修改当前显示的View,如果没有跳转到认证界面,如果有直接跳转到MainView

    3、关键代码
    SAOAuthController.h
    [Objective-C] 纯文本查看 复制代码
    //
    //  SAOAuthController.h
    //  SianWeibo
    //
    //  Created by yusian on 14-4-14.
    //  Copyright (c) 2014年 小龙虾论坛. All rights reserved.
    //
    
    #import <UIKit/UIKit.h>
    
    @interface SAOAuthController : UIViewController
    
    @end
    

    SAOAuthController.m
    [Objective-C] 纯文本查看 复制代码
    //
    //  SAOAuthController.m
    //  SianWeibo
    //
    //  Created by yusian on 14-4-14.
    //  Copyright (c) 2014年 小龙虾论坛. All rights reserved.
    //
    
    #import "SAOAuthController.h"
    #import "SACommon.h"
    #import "SAHttpTool.h"
    #import "SAAccountTool.h"
    #import "SAMainController.h"
    #import "MBProgressHUD.h"
    
    @interface SAOAuthController () <UIWebViewDelegate>
    {
        UIWebView *_webView;
    }
    
    @end
    
    @implementation SAOAuthController
    
    #pragma mark 设置WebView为主View
    -(void)loadView
    {
        _webView = [[UIWebView alloc] initWithFrame:[UIScreen mainScreen].applicationFrame];
        self.view = _webView;
        _webView.delegate = self;
    }
    
    #pragma mark 加载web数据到View
    - (void)viewDidLoad
    {
        [super viewDidLoad];
        // 1 拼接请求授权的URL
        NSString *requestRULString = [NSString stringWithFormat:@"%@?client_id=%@&redirect_uri=%@&display=mobile", kOAuthURL, kAppKey, kRedirect_uri];
        NSURL *url = [NSURL URLWithString:requestRULString];
        
        // 2 将请求的页面加载到WebView
        NSURLRequest *request = [[NSURLRequest alloc] initWithURL:url];
        [_webView loadRequest:request];
    }
    
    #pragma mark 代理方法获取Token
    -(BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
    {
        // 1、获取全路径,将URL转换成字符串
        NSString *url = request.URL.absoluteString;
        
        // 2、查找code范围
        NSRange range = [url rangeOfString:@"code="];       // 匹配包含"code="的URL
        if (range.length){                                  // 如果有,取"code="后跟的串,即为requestToken
            NSInteger index = range.location + range.length;
            NSString *requestToken = [url substringFromIndex:index];
            
        // 3、换取accessToken
            [self getAccessToken:requestToken];
            
            return NO;
        }
        return YES;
    }
    
    -(void)getAccessToken:(NSString *)requestToken
    {
        // SAHttpTool类中自定义方法,实为封装AFNetWorking的方法获取网页数据
        [SAHttpTool httpToolPostWithBaseURL:@"https://api.weibo.com" path:@"oauth2/access_token" params:
         @{
           // 新浪要求必须传递的五个参数
           @"client_id" : kAppKey,
           @"client_secret" : kAppSecret,
           @"grant_type" : @"authorization_code",
           @"code" : requestToken,
           @"redirect_uri" : kRedirect_uri
           
           // 成功获取JSON
           } success:^(id JSON) {
               
               MyLog(@"请求成功");
               
               // 1 将JSON转换成数据模型并归档
               SAAccount *account = [SAAccount accountWithDict:JSON];
               [[SAAccountTool sharedAccountTool] saveAccount:account];
               
               // 2 跳转到主页面
               self.view.window.rootViewController = [[SAMainController alloc]init];
               // MyLog(@"%@", JSON);
               
               // 3 清除页面加载提示
               [MBProgressHUD hideAllHUDsForView:self.view animated:YES];
               
           // 获取信息失败
           } failure:^(NSError *error) {
               
               // 1 打印错误提示
               MyLog(@"请求失败-%@", [error localizedDescription]);
               
               // 2 清除页面加载提示
               [MBProgressHUD hideAllHUDsForView:self.view animated:YES];
           }];
    }
    
    #pragma mark - webView的两个代理方法
    #pragma mark 开始加载页面时调用
    - (void)webViewDidStartLoad:(UIWebView *)webView
    {
        // 引入第三方框架"MBProgressHUD"添加页面加载提示
        MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:webView animated:YES];
        hud.labelText = @"小龙虾加油中...";                    // 设置文字
        hud.labelFont = [UIFont systemFontOfSize:14];
    }
    
    #pragma mark 页面加载完毕后调用
    - (void)webViewDidFinishLoad:(UIWebView *)webView
    {
        // 清除页面加载提示
        [MBProgressHUD hideAllHUDsForView:webView animated:YES];
    }
    
    @end
    

    SAAccount.h
    [Objective-C] 纯文本查看 复制代码
    //
    //  SAAccount.h
    //  SianWeibo
    //
    //  Created by yusian on 14-4-15.
    //  Copyright (c) 2014年 小龙虾论坛. All rights reserved.
    //
    
    #import <Foundation/Foundation.h>
    
    @interface SAAccount : NSObject <NSCoding>
    
    @property (nonatomic, copy) NSString    *accessToken;
    @property (nonatomic, copy) NSString    *expiresIn;
    @property (nonatomic, copy) NSString    *remindIn;
    @property (nonatomic, copy) NSString    *uid;
    
    - (id)initWithDict:(NSDictionary *)dict;
    
    + (id)accountWithDict:(NSDictionary *)dict;
    
    @end
    

    SAAccount.m
    [Objective-C] 纯文本查看 复制代码
    //
    //  SAAccount.m
    //  SianWeibo
    //
    //  Created by yusian on 14-4-15.
    //  Copyright (c) 2014年 小龙虾论坛. All rights reserved.
    //
    
    #import "SAAccount.h"
    
    @implementation SAAccount
    
    - (id)initWithDict:(NSDictionary *)dict
    {
        if (self = [super init])
        {
            self.accessToken = dict[@"access_token"];
            self.expiresIn = dict[@"expires_in"];
            self.remindIn = dict[@"remind_in"];
            self.uid = dict[@"uid"];
        }
        return self;
    }
    
    + (id)accountWithDict:(NSDictionary *)dict
    {
        return [[self alloc] initWithDict:dict];
    }
    
    - (void)encodeWithCoder:(NSCoder *)aCoder
    {
        [aCoder encodeObject:_accessToken forKey:@"accessToken"];
        [aCoder encodeObject:_uid forKey:@"uid"];
    }
    
    - (id)initWithCoder:(NSCoder *)aDecoder
    {
        if (self = [super init]) {
            self.accessToken = [aDecoder decodeObjectForKey:@"accessToken"];
            self.uid = [aDecoder decodeObjectForKey:@"uid"];
        }
        return self;
    }
    
    @end
    

    SAAccountTool.h
    [Objective-C] 纯文本查看 复制代码
    //
    //  SAAccountTool.h
    //  SianWeibo
    //
    //  Created by yusian on 14-4-15.
    //  Copyright (c) 2014年 小龙虾论坛. All rights reserved.
    //
    
    #import <Foundation/Foundation.h>
    #import "SAAccount.h"
    
    @interface SAAccountTool : NSObject
    
    @property (nonatomic, readonly) SAAccount *account;
    
    + (SAAccountTool *)sharedAccountTool;
    
    - (void)saveAccount:(SAAccount *)account;
    
    @end
    

    SAAccountTool.m
    [Objective-C] 纯文本查看 复制代码
    //
    //  SAAccountTool.m
    //  SianWeibo
    //
    //  Created by yusian on 14-4-15.
    //  Copyright (c) 2014年 小龙虾论坛. All rights reserved.
    //
    
    #import "SAAccountTool.h"
    #define kAccountFile [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,YES)[0] stringByAppendingString:@"/account.data"]
    
    @implementation SAAccountTool
    
    // 单例创建三个条件
    // 1、全局实例
    static SAAccountTool *_instance;
    
    // 2、类创建方法
    + (SAAccountTool *)sharedAccountTool
    {
        if (_instance) {
            _instance = [[self alloc] init];
        }
        return  [[self alloc] init];
    }
    
    // 3、重写alloc方法
    + (id)allocWithZone:(struct _NSZone *)zone
    {
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            _instance = [super allocWithZone:zone];
        });
        return _instance;
    }
    
    // 单例在MRC中还需要重写以下三个方法
    /******************************
    -(oneway void)release
    {
        return;
    }
    -(id)retain
    {
        return self;
    }
    -(NSUInteger)retainCount
    {
        return 1;
    }
    *******************************/
    
    - (id)init
    {
        if (self = [super init]) {
            _account = [NSKeyedUnarchiver unarchiveObjectWithFile:kAccountFile];
        }
        return self;
    }
    
    - (void)saveAccount:(SAAccount *)account
    {
        _account = account;
        [NSKeyedArchiver archiveRootObject:account toFile:kAccountFile];
    }
    
    @end
    

    SAHttpTool.h
    [Objective-C] 纯文本查看 复制代码
    //
    //  SAHttpTool.h
    //  SianWeibo
    //
    //  Created by yusian on 14-4-15.
    //  Copyright (c) 2014年 小龙虾论坛. All rights reserved.
    //
    
    #import <Foundation/Foundation.h>
    
    typedef void (^Success)(id JSON);
    typedef void (^Failure)(NSError *error);
    
    @interface SAHttpTool : NSObject
    
    + (void)httpToolPostWithBaseURL:(NSString *)urlString path:(NSString *)pathString params:(NSDictionary *)params success:(Success)success failure:(Failure)failure;
    
    @end
    

    SAHttpTool.m
    [Objective-C] 纯文本查看 复制代码
    //
    //  SAHttpTool.m
    //  SianWeibo
    //
    //  Created by yusian on 14-4-15.
    //  Copyright (c) 2014年 小龙虾论坛. All rights reserved.
    //
    
    #import "SAHttpTool.h"
    #import "AFNetWorking.h"
    
    @implementation SAHttpTool
    
    // 包装第三方框架AFNetWorking,对外提供类似方法
    + (void)httpToolPostWithBaseURL:(NSString *)urlString path:(NSString *)pathString params:(NSDictionary *)params success:(Success)success failure:(Failure)failure
    {
        // 获取授权
        // 1、创建一个Operation
        AFHTTPClient *client = [[AFHTTPClient alloc] initWithBaseURL:[NSURL URLWithString:urlString]];
        NSURLRequest *post = [client requestWithMethod:@"POST" path:pathString parameters:params];
        
        // 2、发送Operation请求
        AFJSONRequestOperation *json = [AFJSONRequestOperation JSONRequestOperationWithRequest:post
                                        
            success:
            ^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {
                
                success(JSON);      // 将方法中的实参传给内部的Success Block调用
            }
                                        
            failure:
            ^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON) {
                
                failure(error);     // 将方法中的error传给内部的Failure Block调用
            }];
        
        [json start];
    }
    
    @end
    


    4、源码下载
    游客,如果您要查看本帖隐藏内容请回复


    相关主题链接
    1、ios实战开发之仿新浪微博(第一讲:新特性展示)
    2、ios实战开发之仿新浪微博(第二讲:主框架搭建)
    3、ios实战开发之仿新浪微博(第三讲:更多界面搭建)
    4、ios实战开发之仿新浪微博(第四讲:OAuth认证)
    5、ios实战开发之仿新浪微博(第五讲:微博数据加载)
    6、ios实战开发之仿新浪微博(第六讲:微博数据展示一)
    7、ios实战开发之仿新浪微博(第七讲:微博数据展示二)
    8、ios实战开发之仿新浪微博(第八讲:微博数据展示三)
    9、ios实战开发之仿新浪微博(第九讲:微博功能完善一)
    10、ios实战开发之仿新浪微博(第十讲:微博功能完善二)
    11、ios实战开发之仿新浪微博(第十一讲:微博功能完善三)
    12、ios实战开发之仿新浪微博(小龙虾发布版)

  • TA的每日心情
    高兴
    2015-10-10 10:21
  • 签到天数: 44 天

    [LV.5]常住居民I

    发表于 2015-7-5 13:15:12 | 显示全部楼层
    给使用AFNetworking2.0以上版本的同学提个醒,新浪返回access_Token数据时,有个坑。
    AFJSONRequestOperation默认不接收text/plain类型的数据,当服务器返回text/plain类型的数据时,会认为出错了。可以通过修改源代码解决问题

    + (NSSet *)acceptableContentTypes {

        return [NSSet setWithObjects:@"text/plain", @"application/json", @"text/json", @"text/javascript", nil];

    }

    该用户从未签到

    发表于 2014-10-24 13:46:43 | 显示全部楼层
    继续感谢!!!!!
    回复

    使用道具 举报

    该用户从未签到

    发表于 2014-11-5 22:12:14 | 显示全部楼层
    看着挺好的,收藏了
  • TA的每日心情
    发光
    2014-11-26 14:19
  • 签到天数: 4 天

    [LV.2]偶尔看看I

    发表于 2014-11-20 10:28:16 | 显示全部楼层
    囫囵吞枣啊、、、、、、、

    该用户从未签到

    发表于 2014-11-23 17:03:53 | 显示全部楼层
    挺好的,学习了
  • TA的每日心情

    2014-12-29 14:23
  • 签到天数: 6 天

    [LV.2]偶尔看看I

    发表于 2014-11-26 10:11:48 | 显示全部楼层
    继续学习怎么进行OAuth认证

    该用户从未签到

    发表于 2014-12-18 17:10:27 | 显示全部楼层
    支持个,学习学习
  • TA的每日心情
    得瑟
    2015-1-8 17:35
  • 签到天数: 3 天

    [LV.2]偶尔看看I

    发表于 2014-12-20 19:59:34 | 显示全部楼层
    继续学习怎么进行OAuth认证

    该用户从未签到

    发表于 2014-12-22 15:52:24 | 显示全部楼层
    请问kOAuthURL、kAppKey、kRedirect_url这三个常量在哪?
  • TA的每日心情
    奋斗
    2022-12-13 21:26
  • 签到天数: 371 天

    [LV.9]以坛为家II

     楼主| 发表于 2014-12-23 08:58:22 | 显示全部楼层
    lihaitao 发表于 2014-12-22 15:52
    请问kOAuthURL、kAppKey、kRedirect_url这三个常量在哪?

    Classes--Other--SACommon.h 工程常量文件
    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    手机版|小黑屋|Archiver|iOS开发笔记 ( 湘ICP备14010846号 )

    GMT+8, 2024-3-29 19:13 , Processed in 0.062113 second(s), 24 queries .

    Powered by Discuz! X3.4

    Copyright © 2001-2021, Tencent Cloud.

    快速回复 返回顶部 返回列表