6

我们有:
(1) 具有 Facebook OAuth 功能的基于 Facebook API 的网络应用程序(“FB 网络应用程序”)
(2)UIWebView基于 iPad 的浏览器(“浏览器”)

我们的目标是在 iPad 上的基于 UIWebView 的浏览器 (2) 中打开 Facebook 登录页面以登录到 FB Web 应用程序 (1)。

这里有一个类似的问题:http:
//facebook.stackoverflow.com/questions/11337285/no-longer-able-to-login-to-ios-app-via-oauth-the-page-requested-was-未找到

但是,该问题的问题发生在用户在 Facebook 表单中输入登录名和密码之后。我们的问题是我们无法首先显示 Facebook 登录表单。如该问题中所建议的,将应用程序类型从“Web”更改为“Native/Desktop”并没有帮助。

步骤: 1. 使用此浏览器
打开我们的网页(简单的 HTML 页面) 2. 点击此页面上的“FB web app”启动按钮 3. OnClick JavaScript 尝试启动 OAuth,这应该会打开 Facebook 的登录屏幕进行登录到 FB 网络应用程序 UIWebView

当前结果(问题):
在 iOS 5.+ 和 iOS 6.+ 设备上
- 我们的网页保持不变
- Facebook 登录页面未显示(我们的网页仍然显示)
在 iOS 4.3 上(按预期工作):
- Facebook登录页面在浏览器的同一UIWebView对象中打开(替换我们的网页)

预期结果:
- 显示 Facebook 登录页面,用户可以输入 Facebook 登录名和密码
- 如果在 iPad 上的 Safari 浏览器中启动,则适用于 iOS 5.+ 和 iOS 6.+。Facebook 登录页面在单独的选项卡中打开(相比之下,没有单独的选项卡UIWebView

问:如何UIWebView在 iOS 5+ 和 iOS 6+ 上打开 Facebook 登录页面以响应 OAuth 请求?

更多技术细节:

我们从内部记录不同的NSURLRequest字段

-(BOOL)webView(UIWebView*)webView shouldStartLoadWithRequest(NSURLREquest*)request navigationType:…   

我们注意到日志中“正确”和“不正确”行为的一些差异。执行流程对我来说是这样的:

首先,我按“FB Web App”启动按钮启动 OAuth,然后有些情况下

iOS 4.3,“正确”<br> 请求 www.facebook.com/dialog/oauth?...
请求 fbwebapp.com
请求 m.facebook.com/login.php?....
--here facebook login出现

iOS 5.0,“不正确1”<br> 对 www.facebook.com/dialog/oauth 的请求?...
对 fbwebapp.com
的请求 对 m.facebook.com/login.php 的请求?...

那么它可能是
——很多 m.facebook.com/login.php?...with next... in parameters
随后是 sqlite 错误——
现在我看到来自 facebook 的“抱歉,出了点问题”页面(这是一个我第一次遇到它)

iOS 6.0 “不正确2”<br> 对 www.facebook.com/dialog/oauth 的
请求?...对 fbwebapp.com 的请求

-(void)webView:(UIWebView*)webView didFailLoadWithError:(NSError*)error 被调用,错误代码为 -999

您可以看到该行为肯定取决于 iOS 版本。但常见的情况是在获取 m.facebook.com/login.php.. URL 的步骤中发生错误。但这就是我们能检测到的全部。

我们整天都在撞墙寻找解决方案。无可救药。
您能帮我们打开 Facebook 登录页面UIWebView以响应 OAuth 吗?

4

5 回答 5

4

只需使用:此代码

if (![FBSDKAccessToken currentAccessToken]) 
{   
    FBSDKLoginManager *manager = [[FBSDKLoginManager alloc]init];
    manager.loginBehavior = FBSDKLoginBehaviorWeb;
    [manager logInWithReadPermissions:@[@"public_profile", @"email", @"user_friends"] handler:
     ^(FBSDKLoginManagerLoginResult *result, NSError *error) {

         NSLog(@"result.token: %@",result.token.tokenString);  
         NSLog(@"%@",result.token.userID);   
         NSLog(@"%hhd",result.isCancelled);     
     }];
}

// 这里manager.loginBehavior = FBSDKLoginBehaviorWeb;是你打开 facebook 所需要的一切UIWebview

于 2015-05-21T11:10:10.897 回答
3

做到了!

这有点像 hack,但是 iOS 6 上 UiWebView 上的 js facebook sdk 登录终于可以工作了。

怎么可能做到?它是一个纯 JS + Facebook JS SDK + UIWebView Delegate 处理功能的解决方案。

JS - 第一步)

登录按钮(用于连接 facebook)调用此函数示例,这将触发 Face JS 登录/oauth 对话框:

function loginWithFacebookClick(){

FB.login(function(response){
    //normal browsers callback 
});

}

JS - 第二步)

每次用户加载网页时(在 FB.init() 之后)添加一个 authResponseChange 监听器以捕获用户的连接状态:

FB.Event.subscribe('auth.authResponse.Change', function(response){
//UIWebView login 'callback' handler
var auth = response.authResponse;
if(auth.status == 'connected'){
    //user is connected with facebook! just log him at your webapp
}
});

并且使用应用程序的 UIWebView 委托函数,您可以处理 facebook oauth 响应

目标 C - 第三步)

- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
NSString *url = [[request URL] absoluteString];

//when user status == connected (has a access_token at facebook oauth response)
if([url hasPrefix:@"https://m.facebook.com/dialog/oauth"] && [url rangeOfString:@"access_token="].location != NSNotFound)
{
    [self backToLastPage];
    return NO;
}

return YES;
}

- (void)webViewDidFinishLoad:(UIWebView *)webView
{

NSString *url = [[webView.request URL] absoluteString];

if([url hasPrefix:@"https://m.facebook.com/dialog/oauth"])
{
    NSString *bodyHTML = [webView stringByEvaluatingJavaScriptFromString:@"document.body.innerHTML"];

    //Facebook oauth response dead end: is a blank body and a head with a script that does 
    //nothing. But if you got back to your last page, the handler of authResponseChange
    //will catch a connected status if user did his login and auth app
    if([bodyHTML isEqualToString:@""])
    {
        [self backToLastPage];
    }
}

}

因此,当“重定向”用户到最后加载的页面时,第二步是在 facebook 登录对话框中处理用户操作。

如果我对这个答案太快了,请问我!希望能帮助到你。

于 2013-07-21T00:12:45.513 回答
3

如果有人在谷歌搜索,这对我有用:

-(BOOL) webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)inType {
    if ([request.URL.absoluteString containsString:@"m.facebook.com"]) {
        if ([request.URL.absoluteString rangeOfString:@"back"].location == 0) {
            [self.popUp removeFromSuperview];
            self.popUp = nil;
            return NO;
        }
        if (self.popUp) {
            return YES;
        }

        UIWebView *wv = [self popUpWebView];
        [wv loadRequest:request];
        return NO;
    }
    return YES;
}

- (UIWebView *) popUpWebView {
    toolbar height
    UIWebView *webView = [[UIWebView alloc]
            initWithFrame:CGRectMake(0, 0, (float)self.view.bounds.size.width,
                    (float)self.view.bounds.size.height)];
    webView.scalesPageToFit = YES;
    webView.delegate = self;
    // Add to windows array and make active window
    self.popUp = webView;
    [self.view addSubview:webView];
    return webView;
}

- (void)webViewDidFinishLoad:(UIWebView *)webView {

    if (self.popUp) {
        NSError *error = nil;
        NSString *jsFromFile = @"window.close=function(){window.location.assign('back://' + window.location);};";
        __unused NSString *jsOverrides = [webView
                stringByEvaluatingJavaScriptFromString:jsFromFile];

        JSContext *openerContext = [self.webView
                valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
        JSContext *popupContext = [webView
                valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
        popupContext[@"window"][@"opener"] = openerContext[@"window"];
    }


  //this is the secret sauce  
  if (webView == self.popUp
            && [webView.request.URL.absoluteString containsString:@"m.facebook.com"]
            && [[webView stringByEvaluatingJavaScriptFromString:@"document.body.innerHTML"] isEqualToString:@""]) {
        [webView stringByEvaluatingJavaScriptFromString:@"eval(document.getElementsByTagName('script')[0].text)"];
    }
}

我从这里抓到了一堆这个实现

根据您的 Web 实施,可能会有一个额外的步骤。Facebook 脚本实际上执行 a window.close()then a window.open()then a window.close()。对我来说,这引起了问题,因为在 Web 端,在此登录完成后,我的窗口(即我希望用户登录的 webView)收到window.close()来自 Facebook SDK 的呼叫。我假设这是因为 Facebook SDK 期望该window.open()调用打开一个将关闭的新窗口。

由于我们没有覆盖 的功能window.open(),调用window.open()不会做任何事情,Facebook SDK 将尝试关闭您的窗口。这可能会导致各种问题,但对我来说,因为我使用的是 Parse,window.localStorage所以null我遇到了各种错误。

如果您遇到类似的情况,您有两种选择来解决它:

  1. 如果您可以控制 Web 代码,并且想进行一些小技巧,请把它扔进去window.close=function(){}
  2. 如果您无法控制 Web 代码,您可以window.close像我们为 popUp webView 所做的那样为主 webView 添加覆盖,或者覆盖window.open打开另一个弹出窗口的功能(在此处更详细地描述
于 2016-01-13T21:47:40.310 回答
0

使用 FBDialog 类来提示用户登录。这在应用程序内部使用 webview,因此在成功登录后,用户将在任何 UIWebView 内部登录:

NSString *kRedirectURL  = @"fbconnect://success";
NSString *kSDK = @"ios" ;
NSString *kLogin = @"oauth";
NSString *kDialogBaseURL = @"https://m.facebook.com/dialog/";

NSMutableDictionary* params = [NSMutableDictionary dictionaryWithObjectsAndKeys:
                               AE_FACEBOOK_APPID, @"client_id",
                               @"user_agent", @"type",
                               kRedirectURL, @"redirect_uri",
                               @"touch", @"display",
                               kSDK, @"sdk",
                               nil];

NSString *loginDialogURL = [kDialogBaseURL stringByAppendingString:kLogin];

FBLoginDialog* loginDialog = [[FBLoginDialog alloc] initWithURL:loginDialogURL
                                                    loginParams:params
                                                       delegate:self];
[loginDialog show];

然后让你的类遵守 FBDialogDelegate 协议,并将这个函数添加到你的类中:

-(void)fbDialogLogin: (NSString *)token expirationDate:(NSDate *)expirationDate{
    // Store the token and expiration date into the FB SDK
    self.facebook.accessToken = token;
    self.facebook.expirationDate = expirationDate;
    // Then persist these so the SDK picks them up on next load

    NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];

[defaults setObject:self.facebook.accessToken forKey:ACCESS_TOKEN_KEY];
[defaults setObject:self.facebook.expirationDate forKey:EXPIRATION_DATE_KEY];
[defaults synchronize];
}

于 2013-01-11T16:59:36.070 回答
0

如何在 UIWebView 中登录 Facebook。

目标-c

使用泰勒斯汀的答案。他拯救了我的一天。谢谢泰勒斯汀_

但我使用的是 Swift 3。所以我只是从 taylorstine 的答案转换了下面的代码。

斯威夫特 3。

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

    if let _ = request.url?.absoluteString.range(of: "m.facebook.com" ) {
        if let _ = request.url?.absoluteString.range(of: "back"){
            self.popUp?.removeFromSuperview()
            self.popUp = nil
            return false
        }

        if let _ = self.popUp {
            return true
        }

        let wv = popUpWebView()
        wv.loadRequest(request)

        return false
    }

    return true
}

func popUpWebView() -> UIWebView {

    let webView = UIWebView(frame: self.view.frame)

    webView.delegate = self
    self.popUp = webView
    self.view.addSubview(webView)

    return webView
}

func webViewDidFinishLoad(_ webView: UIWebView) {
    if let _ = self.popUp {

        let jsFromFile = "window.close=function(){window.location.assign('back://' + window.location);};"

        let _ = webView.stringByEvaluatingJavaScript(from: jsFromFile)

        let openerContext = self.webView.value(forKeyPath: "documentView.webView.mainFrame.javaScriptContext") as! JSContext

        let popupContext = webView.value(forKeyPath: "documentView.webView.mainFrame.javaScriptContext") as! JSContext

        popupContext.setObject("opener", forKeyedSubscript: "window" as (NSCopying & NSObjectProtocol)!)
        popupContext.setObject(openerContext.objectForKeyedSubscript("window"), forKeyedSubscript: "opener"  as (NSCopying & NSObjectProtocol)!)

    }

    if webView == self.popUp
        && (webView.request?.url?.absoluteString.range(of:"m.facebook.com") != nil)
        && webView.stringByEvaluatingJavaScript(from: "document.body.innerHTML") == "" {

        webView.stringByEvaluatingJavaScript(from: "eval(document.getElementsByTagName('script')[0].text)")

    }

}
于 2017-02-08T08:37:34.873 回答