【问题标题】:How to handle OAuth URL callbacks with Intent filters if authentication is done with webview?如果使用 webview 完成身份验证,如何使用 Intent 过滤器处理 OAuth URL 回调?
【发布时间】:2017-08-09 17:31:06
【问题描述】:

我正在开发一个使用 OAuth 进行身份验证的应用,但我在处理 OAuth 回调时遇到了一点问题。

身份验证

我的应用程序有一个 webview 作为登录屏幕,并且我获得了一个 url,可以在我的 webview 中加载 auth form。假设网址是:

https://myoauthhost.com/oauth/auth?response_type=code&client_id=XXXXXXX&redirect_uri=YYYYYYYY&scope=ZZZZZZZZZZ

在身份验证活动 (AuthActivity.java) 中,我有以下内容:

    String authURL = https://myoauthhost.com/oauth/auth?response_type=code&client_id=XXXXXXX&redirect_uri=YYYYYYYY&scope=ZZZZZZZZZZ
    myWebView.loadUrl(authURL);

在 manifest.xml 中,我有以下 oauth 回调处理:

<activity
            android:name=".AuthActivity"
            android:label="@string/app_name"
            android:screenOrientation="portrait" >

            <intent-filter>
                <action android:name="android.intent.action.VIEW" />

                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="android.intent.category.BROWSABLE" />

                <data
                    android:host="authprovider"
                    android:scheme="auth" />
            </intent-filter>
</activity>

问题

在 webview 中使用此 url(使用 loadURL() 方法)会重定向到另一个包含 REAL OAUTH WEB FROM 的 url(应该在 webview 中加载)。问题是这个 redirection 会自动启动 Android 中的 intent selection :由于 URL 应该由 Web 浏览器处理,Android 允许您选择一个可用的 Web 浏览器手机打开网址。

由于这不是我想要的,我必须包含以下代码,以便在 webview 中处理重定向但不启动 web 浏览器(或其他):

myWebView.setWebViewClient(new WebViewClient());

因此,使用此代码,重定向是“在 web 视图内”处理的,并且我显示了登录屏幕。

然后我可以输入凭据(例如:通过 Twitter 的 oauth),但是当身份验证完成时,会收到回调,但是应该处理回调的活动(AuthActivity 配置为在清单中接收回调)是未启动。相反,我让 webview 显示一条消息,指出找不到 url 回调(在我们的例子中:authprovider://auth/XXX?xxx=yyy,如清单中配置的那样)。

原因可能是以下代码:

myWebView.setWebViewClient(new WebViewClient());

前面介绍过,告诉Android webview 处理一切。所以现在,由于回调url不是web url,所以处理起来很麻烦,甚至无法启动可以处理的intent。

问题

我该如何解决这个问题? 我应该能够让活动处理回调,但不能让 webview 尝试加载它。

任何帮助将不胜感激

提前致谢

【问题讨论】:

    标签: android authentication webview callback oauth-2.0


    【解决方案1】:

    首先在清单中,将这些属性设置为启动 WebView 的活动

    android:launchMode="singleInstance" 
    

    并为其添加一个意图过滤器

    <intent-filter>
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
        <data android:scheme="oauth-testing" />
    </intent-filter>
    

    然后在你的代码中当用户点击登录按钮时

    mReqToken = mTwitter.getOAuthRequestToken(CALLBACK_URL);
    WebView webView = new WebView(this);
    webView.requestFocus(View.FOCUS_DOWN);
    webView.setOnTouchListener(new View.OnTouchListener() {
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                case MotionEvent.ACTION_UP:
                if (!v.hasFocus()) {
                    v.requestFocus();
                }
                break;
            }
            return false;
        }
    });
    webView.loadUrl(mReqToken.getAuthenticationURL());
    mainLayout.removeAllViews();
    mainLayout.addView(webView);
    

    这里的回调地址是
    private static final String CALLBACK_URL = "oauth-testing:///";
    并且您正在创建一个动态 web 视图并向用户显示。登录后,webview 关闭,代码到达onNewIntent()。您需要在登录后实现您的功能。

    @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        dealWithTwitterResponse(intent);
    }  
    private void dealWithTwitterResponse(Intent intent) {
        Uri uri = intent.getData();
        System.out.println("URI=" + uri);
        if (uri != null && uri.toString().startsWith(CALLBACK_URL)) {
            String oauthVerifier = uri.getQueryParameter("oauth_verifier");
            authoriseNewUser(oauthVerifier);
        }
    }
    

    我知道我添加了很多代码 sn-p,其中一些可能不相关,但我希望有一天它会对某人有所帮助。

    【讨论】:

      【解决方案2】:

      好吧,首先您可能需要调用您的服务提供商提供的 URL,如果存在任何重定向,您将获得 HTTP 状态代码 3xx。接下来,如果存在任何重定向,您可以尝试调用实际 URL。对于正常的 HTTP 响应,您将获得 HTTP 2xx 状态码。

      【讨论】:

      • 我有一个不同的情况,尝试将 OAuth 与 AccountManagerandroid:launchMode="singleInstance" 集成为我工作。
      【解决方案3】:

      因为你有 WebView 的对象,所以你可以重载它,然后通过这两种方式处理你想要导航到 Activity 的 url。

      private  class MyWebView extends WebViewClient{   
                  @TargetApi(Build.VERSION_CODES.N)
                  @Override
                  public boolean shouldOverrideUrlLoading(WebView webView, WebResourceRequest request)
                  {
                      Uri uri = request.getUrl();
                      return shouldOverrideUrlLoading(uri.toString());
                  }
      
                  private boolean shouldOverrideUrlLoading(final String url)
                  {
                      Log.i(TAG, "shouldOverrideUrlLoading() URL : " + url);
                         //your code to handle the url for navigation will go here
                      try{
                            if(url.contains("yourcondition")){
                              //1st way to handle 
                              Intent intent = new Intent(Intent.ACTION_VIEW);
                              intent.setData(Uri.parse(url));
                              YourActivity.this.startActivity(intent);
                             }else{
                                 //2nd way 
                              Intent intent = new Intent(YourActivity.this,NewActivity.class);
                              //you can set the data 
                              intent.setData(Uri.parse(url));
                              YourActivity.this.startActivity(intent);
                             }
                           }catch(ActivityNotFoundException e) {    
                             Log.e(LOGTAG,"Could not load url"+url);
                          }                    
                     return false;
                     // return true; // this True means that application wants to leave the WebView and try to go to browser and handle the url itself, otherwise return false.
                   } 
          @Override
          public boolean shouldOverrideUrlLoading(WebView view, String url) 
          {
      
      
              try {
                    if(url.contains("yourcondition")){
                      //1st way to handle 
                    Intent intent = new Intent(Intent.ACTION_VIEW);
                    intent.setData(Uri.parse(url));
                    YourActivity.this.startActivity(intent);
                  }else{
                 //2nd way 
                  Intent intent = new Intent(YourActivity.this,NewActivity.class);
                 //you can set the data 
                  intent.setData(Uri.parse(url));
                  YourActivity.this.startActivity(intent);
                  }
      
              }   catch(ActivityNotFoundException e) {
      
                  Log.e(LOGTAG,"Could not load url"+url);
              }
      
              return false;     
          }
      }
      

      您可以设置默认的mWebView.setWebViewClient(new WebViewClient());,但可以通过 只需添加一个默认的自定义 WebViewClient 将只允许 WebView 自己处理任何加载的 url,即在 webView 中而不是浏览器中。

      但是如果你通过上面的class MyWebViewmWebView.setWebViewClient(new MyWebView()); 一样超载,那么你可以控制 url 的加载同样的事情你可以实现

      myWebView.setWebViewClient(new WebViewClient(){ 
      
                      @TargetApi(Build.VERSION_CODES.N)
                      @Override
                      public boolean shouldOverrideUrlLoading(WebView webView, WebResourceRequest request)
                      {
                          Uri uri = request.getUrl();
                          return shouldOverrideUrlLoading(uri.toString());
                      }
      
                      private boolean shouldOverrideUrlLoading(final String url)
                      {
                          Log.i(TAG, "shouldOverrideUrlLoading() URL : " + url);
                             //your code to handle the url for navigation will go here
                          try{
                                if(url.contains("yourcondition")){
                                  //1st way to handle 
                                  Intent intent = new Intent(Intent.ACTION_VIEW);
                                  intent.setData(Uri.parse(url));
                                  YourActivity.this.startActivity(intent);
                                 }else{
                                     //2nd way 
                                  Intent intent = new Intent(YourActivity.this,NewActivity.class);
                                  //you can set the data 
                                  intent.setData(Uri.parse(url));
                                  YourActivity.this.startActivity(intent);
                                 }
                               }catch(ActivityNotFoundException e) {    
                                 Log.e(LOGTAG,"Could not load url"+url);
                              }                    
                         return false;
                         // return true; // this True means that application wants to leave the WebView and try to go to browser and handle the url itself, otherwise return false.
                       } 
      
          @Override
          public boolean shouldOverrideUrlLoading(WebView view, String url) 
          {
      
              try {
                   if(url.contains("yourcondition")){
                   //1st way to handle
                  Intent intent = new Intent(Intent.ACTION_VIEW);
                  intent.setData(Uri.parse(url));
                  YourActivity.this.startActivity(intent);
                  }else{
                   //2nd way 
                      Intent intent = new Intent(YourActivity.this,NewActivity.class);
                 //you can set the data 
                  intent.setData(Uri.parse(url));
                  YourActivity.this.startActivity(intent);
                 }    
              }   catch(ActivityNotFoundException e) {
      
                  Log.e(LOGTAG,"Could not load url"+url);
              }
      
              return false;      
      
          }});
      

      如果您无法控制 webview 对象,那么这不是我将在解决此问题后更新的解决方案。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-10-29
        • 1970-01-01
        • 2011-02-09
        • 1970-01-01
        • 2011-08-11
        • 2019-04-11
        • 2011-06-20
        • 2020-09-19
        相关资源
        最近更新 更多