OAuth授权
OAuth授权分四步:
第一步,应用向服务提供方申请请求令牌(Request Token),服务提供方验证通过后将令牌返回。这个步骤由于涉及到应用帐号密码,在应用的服务端发起,所以这个步骤对用户透明。
第二步,应用使用请求令牌让浏览器重定向到服务提供方进行登录验证和授权。服务提供方校验请求令牌,将第三方的资料显示给用户,提示用户选择同意或拒绝此次授权。如果用户同意授权,发放已授权令牌并将用户引导到当前应用的注册地址。这个步骤从重定向开始到引导回注册地址之前,应用方并不参与用户身份校验和授权过程,确保第三方不可获得用户的真实帐号密码。
第三步,用已授权令牌向服务提供方换取ATOK。第三方应用需在服务端发起请求,用帐号密码和上一步的令牌换取ATOK,这个步骤对用户而言也是透明的。如果前两步分别是让服务提供方认证应用和用户,那这步就是用户和服务提供方再次认证第三方应用。因为用户浏览器将第二步的结果重定向到第三步,除非用户DNS被劫持,否则就能确保重定向到的是合法的地址。曾经我很困惑在用户授权之后为何不直接返回ATOK而需要再次换取,估计是出于对ATOK的安全考虑,用户浏览器一端存在太多的可能性让ATOK泄漏,最安全的办法还是让第三方服务端来获取和保管ATOK。
第四步,用ATOK作为令牌访问受保护资源。很多时候,权限是有多种类别的。ATOK包含了某个用户对某个应用的授权凭据,准确的说,ATOK对应用户授权时所赋予的一系列权限的集合。所以在这一步,除了校验ATOK的合法性之外,服务提供方还需对该ATOK是否拥有足够的权限执行被保护操作进行判断。
为什么要OAuth认证?
(引述别人的话)
如果你开车去酒店赴宴,你经常会苦于找不到停车位而耽误很多时间。是否有好办法可以避免这个问题呢?有的,听说有一些豪车的车主就不担心这个问题。豪车一般配备两种钥匙:主钥匙和泊车钥匙。当你到酒店后,只需要将泊车钥匙交给服务生,停车的事情就由服务生去处理。与主钥匙相比,这种泊车钥匙的使用功能是受限制的:它只能启动发动机并让车行驶一段有限的距离,可以锁车,但无法打开后备箱,无法使用车内其他设备。这里就体现了一种简单的“开放授权”思想:通过一把泊车钥匙,车主便能将汽车的部分使用功能(如启动发动机、行驶一段有限的距离)授权给服务生。
Request Token URL: 获取未授权的Request Token服务地址;
User Authorization URL: 获取用户授权的Request Token服务地址;
Access Token URL: 用授权的Request Token换取Access Token的服务地址;
新浪微博OAuth在iOS客户端中的应用
NSString *string = [NSString stringWithFormat:@"https://api.weibo.com/oauth2/authorize?client_id=%@&redirect_uri=%@&display=mobile",APPKEY,REDIRECT_URL]; NSURL *url = [NSURL URLWithString:string]; //用户授权请求 NSURLRequest *request = [NSURLRequest requestWithURL:url]; //加载请求界面 [_webView loadRequest:request];
执行上面的代码我们会调出下面的界面
输入账号信息,点击登录:
点击授权的时候,我们可以在代理方法中打印中请求对象的url.
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType { NSLog(@"网页加载请求的时候加载此方法"); //根据请求对象,获取请求url //absoluteString把url转化成字符串 NSString *string = request.URL.absoluteString; NSLog(@"%@",string); return YES; }
打印信息为:
2015-09-22 13:12:14.339 weibo[1907:60b] http://www.cnblogs.com/wangyaoguo?code=16cca5f05d27854d0341c76cb334390e
我们可以提取出code信息。
然后根据code,发起请求,获取access_token,然后根据access_token发起请求,获取微博数据。
下面的代码是发一条微博的代码段:
#import "ViewController.h" #import "ASIFormDataRequest.h" #define APPKEY @"861851961" #define APPSECRET @"875e13af3aa6cb99c69a037be7257ecf" #define REDIRECT_URL @"http://www.cnblogs.com/wangyaoguo" @interface ViewController () @end @implementation ViewController //可以载入授权界面 - (void)viewDidLoad { [super viewDidLoad]; //拼接请求地址 NSString *string = [NSString stringWithFormat:@"https://api.weibo.com/oauth2/authorize?client_id=%@&redirect_uri=%@&display=mobile",APPKEY,REDIRECT_URL]; //将字符串转成url NSURL *url = [NSURL URLWithString:string]; //用户授权请求 NSURLRequest *request = [NSURLRequest requestWithURL:url]; //加载请求界面,载入输入密码的界面 [_webView loadRequest:request]; _webView.delegate = self; } //点击授权后,可以获取code值,根据code发起一条请求 - (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType { //根据请求对象,获取请求url //absoluteString把url转化成字符串 //用户授权后,从返回值中取出code,根据code,获取访问令牌 NSString *string = request.URL.absoluteString; NSRange range = [string rangeOfString:@"code"]; if (range.location != NSNotFound) { //以code=字符串对string分隔 NSString *code =[string componentsSeparatedByString:@"code="][1]; //code = [code stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; //获取授权服务器,获取通行证 NSURL *url = [NSURL URLWithString:@"https://api.weibo.com/oauth2/access_token"]; ASIFormDataRequest *formrequest = [ASIFormDataRequest requestWithURL:url]; formrequest.tag = 11; [formrequest setPostValue:APPKEY forKey:@"client_id"]; [formrequest setPostValue:APPSECRET forKey:@"client_secret"]; [formrequest setPostValue:@"authorization_code" forKey:@"grant_type"]; [formrequest setPostValue:code forKey:@"code"]; [formrequest setPostValue:REDIRECT_URL forKey:@"redirect_uri"]; //发起异步请求 formrequest.delegate = self; [formrequest startAsynchronous]; } return YES; } //从返回的信息中,提取access_token,然后根据access_token发一条微博 - (void)requestFinished:(ASIHTTPRequest *)request; { if (request.tag == 11) { NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:request.responseData options:NSJSONReadingMutableContainers error:nil]; //获取数据的通行证(令牌) _accessToken = [dic objectForKey:@"access_token"]; //NSString *expireIn = [dic objectForKey:@"expires_in"]; //通行证的生命周期(过期时间,5年) // NSDate *date = [NSDate dateWithTimeIntervalSinceNow:[expireIn doubleValue]]; // NSLog(@"date:%@",date); //使用拿到的通行证发微博 [self sendSinaWeibo]; } } //发新浪微博 -(void)sendSinaWeibo { //使用令牌发布一条微博 ASIFormDataRequest *sendRequest = [ASIFormDataRequest requestWithURL:[NSURL URLWithString:@"https://api.weibo.com/2/statuses/update.json"]]; sendRequest.tag = 12; [sendRequest setPostValue:_accessToken forKey:@"access_token"]; [sendRequest setPostValue:@"ios客户端我的第一条微博" forKey:@"status"]; [sendRequest startAsynchronous]; }