flutter 踩坑(二):获取webview中cookie的session

需求

由于最近要获取阿里系网站的信息,网站在登录模块做了大量的保护工作,我的需求是拿到登录之后的cookie以获得session字段的值,用来保持登录状态。所以想通过webview来获取登录的信息。
在flutter上面比较流行的一个webview插件为:flutter_webview_plugin

flutter_webview_plugin 使用

1.在app的路由上面添加登录的页面

routes: {
        "/web_login":(_) => new WebviewScaffold(url: Config.login,
        withLocalStorage: true,
        hidden: true,
        userAgent: "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Mobile Safari/537.36",
        appBar: new AppBar(
          centerTitle: true ,
          title: new Text("登录"),
        ),
        initialChild: Container(
          child: new Center(
            child: new CupertinoActivityIndicator(animating: true,),
          ),
        ),),

注意:userAgent字段一定要加上,不然在ios下打开WKwebview会白屏

2.初始化flutterWebviewPlugin,作为全局变量;并且在启动app时如果没登录就跳转到登录界面

final flutterWebviewPlugin = new FlutterWebviewPlugin();
if(_event.data.length == 0){
        print("nologin ");
        Navigator.of(context).pushNamed("/web_login");
        _webViewLogin();
      }else{
        print("get events data successfully");
      }

3.监听url的变化,监测到跳转到成功之后的界面后获取cookies然后保存到sp中

flutterWebviewPlugin.onUrlChanged.listen((String url){
      var _uri = Uri.parse(url);
      if(_uri.path == "/dashboard"){
        Navigator.of(context).pop();
        flutterWebviewPlugin.close();
        print("登陆成功");
        flutterWebviewPlugin.getCookies().then((Map<String,String> _cookies){
          print(_cookies);
          _saveCookie();
        });
      }
    });

以为这样就大功告成,但是打印出来的cookie其实是不包含session字段的。
看一下getCookies()方法的源码:

Future<Map<String, String>> getCookies() async {
    final cookiesString = await evalJavascript('document.cookie');
    final cookies = <String, String>{};

    if (cookiesString?.isNotEmpty == true) {
      cookiesString.split(';').forEach((String cookie) {
        final split = cookie.split('=');
        cookies[split[0]] = split[1];
      });
    }

    return cookies;
  }

通过js来获得cookie,大多数网站都会做跨域安全处理,这样基本上不可能会获得session的值,那就毫无意义。

改轮子

目前flutter 插件中并没有可以直接获取webview cookie插件,因此只能自己改轮子
flutter_webview_plugin 使用的是 webviewwkwebview;

修改插件的方式为:

Android Studio左侧projet窗口——External Libraries——Flutter Plugins——flutter_webview_plugin

Android获取cookie实现:

1.修改flutter_webview_plugin/android文件夹下的WebviewManager.java
新加一个方法:

void getAllCookies(MethodCall call, final MethodChannel.Result result){
        String url = call.argument("url");
        CookieManager cookieManager = CookieManager.getInstance();
        String cookieStr = cookieManager.getCookie(url);
        result.success(cookieStr);
    }

2.修改flutter_webview_plugin/android文件夹下的FlutterWebviewPlugin.java

@Override
    public void onMethodCall(MethodCall call, MethodChannel.Result result) {
        switch (call.method) {
            case "launch":
                openUrl(call, result);
                break;
            case "close":
                close(call, result);
                break;
            case "eval":
                eval(call, result);
                break;
            case "resize":
                resize(call, result);
                break;
            case "reload":
                reload(call, result);
                break;
            case "back":
                back(call, result);
                break;
            case "forward":
                forward(call, result);
                break;
            case "hide":
                hide(call, result);
                break;
            case "show":
                show(call, result);
                break;
            case "reloadUrl":
                reloadUrl(call, result);
                break;
            case "stopLoading":
                stopLoading(call, result);
                break;
            case "cleanCookies":
                cleanCookies(call, result);
                break;
            case "getAllCookies":
                getAllCookies(call,result);
                 break;
            default:
                result.notImplemented();
                break;
        }
    }

在onMethodCall方法上新增case的分支,用于判断使用的方法是getAllCookies

3.新增一个getAllCookies的方法

private void getAllCookies(MethodCall call, final MethodChannel.Result result){
        if (webViewManager != null){
            webViewManager.getAllCookies(call,result);
        }
    }

IOS获取Cookie的实现

1.修改flutter_webview_plugin/ios/Classes文件夹下的FlutterWebviewPlugin.m

- (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result {
    if ([@"launch" isEqualToString:call.method]) {
        if (!self.webview)
            [self initWebview:call];
        else
            [self navigate:call];
        result(nil);
    } else if ([@"close" isEqualToString:call.method]) {
        [self closeWebView];
        result(nil);
    } else if ([@"eval" isEqualToString:call.method]) {
        [self evalJavascript:call completionHandler:^(NSString * response) {
            result(response);
        }];
    } else if ([@"resize" isEqualToString:call.method]) {
        [self resize:call];
        result(nil);
    } else if ([@"reloadUrl" isEqualToString:call.method]) {
        [self reloadUrl:call];
        result(nil);
    } else if ([@"show" isEqualToString:call.method]) {
        [self show];
        result(nil);
    } else if ([@"hide" isEqualToString:call.method]) {
        [self hide];
        result(nil);
    } else if ([@"stopLoading" isEqualToString:call.method]) {
        [self stopLoading];
        result(nil);
    } else if ([@"cleanCookies" isEqualToString:call.method]) {
        [self cleanCookies];
    } else if ([@"back" isEqualToString:call.method]) {
        [self back];
        result(nil);
    } else if ([@"forward" isEqualToString:call.method]) {
        [self forward];
        result(nil);
    } else if ([@"reload" isEqualToString:call.method]) {
        [self reload];
        result(nil);
    }else if ([@"getAllCookies" isEqualToString:call.method]){
        [self getAllCookies:call completionHandler:^(NSString *cookies) {
              result(cookies);
          }];
    }else {
        result(FlutterMethodNotImplemented);
    }
}

添加一个getAllCookies的分支判断

2.添加一个方法
仅支持ios11以上

- (void)getAllCookies:(FlutterMethodCall*)call
     completionHandler:(void (^_Nullable)(NSString * cookies))completionHandler {
    if (self.webview != nil) {
        NSString *url = call.arguments[@"url"];
        WKHTTPCookieStore *cookieStore = self.webview.configuration.websiteDataStore.httpCookieStore;
        [cookieStore getAllCookies:^(NSArray<NSHTTPCookie *> * _Nonnull cookies) {
            NSString *allCookies = @"";
            NSEnumerator *cookie_enum = [cookies objectEnumerator];
            NSHTTPCookie *temp_cookie;
            while (temp_cookie = [cookie_enum nextObject]) {
                NSString *temp = [NSString stringWithFormat:@"%@=%@;",[temp_cookie name],[temp_cookie value]];
                allCookies = [allCookies stringByAppendingString:temp];
            }
            completionHandler([NSString stringWithFormat:@"%@", allCookies]);
        }];
    } else {
        completionHandler(nil);
    }
}

Dart修改

修改flutter_webview_plugin/lib/src文件夹下的base.dart
添加getAllCookies的方法

/// Get AllCookies
  Future<String> getAllCookies(String url) async {
    final res = await _channel.invokeMethod('getAllCookies', {'url': url});
    return res;
  }

使用

  Future<Null> _getAllCookies() async {
    try {
      final String result = await flutterWebviewPlugin.getAllCookies("https://www.yuque.com/dashboard");
      print(result);
      cookie = result;
      _saveCookie();
      _getHttpData();
    } on PlatformException catch (e) {
      cookie = "";
    }
  }

注意:getAllCookies必须在浏览器未关闭之前获取,否则会获取空值

    原文作者:DarknessYami
    原文地址: https://www.jianshu.com/p/55092eb06c17
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞