使用Android中的Retrofit2进行Java OAuth身份验证(7.1.1 API 25)

时间:2018-03-27 14:13:17

标签: android android-intent oauth-2.0 retrofit retrofit2

在Android(Android 7.1.1 API 25)中,我尝试使用下面代码段1中显示的类中的Retrofit2库进行OAuth2身份验证。在onCreate方法中,我为了发送授权请求而启动一个intent,而在onResume方法中,我试图捕获OAuth服务器应该返回的授权代码,如果事情没有问题的话。为了捕获授权代码,我在android清单文件中指定了一个intent-filter,如下面的代码段2所示。

在代码段1中的onResume方法中,从getIntent()。getData()调用返回的uri对象始终返回null。因此,执行永远不会进入以下if块,我无法检索授权代码。考虑到代码片段1和2中提供的内容,请您告诉我我在这里缺少什么?感谢。

SNIPPET 1

package com.xyz.sdk;

import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.View;
import android.view.Menu;
import android.view.MenuItem;


public class MainActivity extends AppCompatActivity {
private final static String CLIENT_ID = "client";
private final static String CLIENT_SECRET = "secret";

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
    setSupportActionBar(toolbar);

    FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
    fab.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
                    .setAction("Action", null).show();
        }
    });

    StringBuilder authURLBuilder = new StringBuilder();

    authURLBuilder.append("https://id.xyz.com/api/connect/authorize").append("?").
            append("grant_type=").append("password").
            append("&username=").append("86110").
            append("&client_id=").append(CLIENT_ID).
            append("&scope=").append("transfers public accounts offline_access").
            append("&response_type=code&redirect_uri=").
            append("myauth://mycallback");

    /*BEGIN: THIS IS WHERE I AM SENDING AUTHORIZATION REQUEST */
    Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(authURLBuilder.toString()));
    intent.setPackage(this.getClass().getPackage().getName());
    startService(intent);
    /*END: THIS IS WHERE I AM SENDING AUTHORIZATION REQUEST */
}

@Override
protected void onResume(){
    super.onResume();
    Uri uri = getIntent().getData(); //THIS ALWAYS RETURN NULL
    if(uri != null && uri.toString().startsWith(("myauth://mycallback"))) {
        String code = uri.getQueryParameter("code");
        //this.onAuthCodeResponseReceived(code);
    }
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    // Inflate the menu; this adds items to the action bar if it is present.
    getMenuInflater().inflate(R.menu.menu_main, menu);
    return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    // Handle action bar item clicks here. The action bar will
    // automatically handle clicks on the Home/Up button, so long
    // as you specify a parent activity in AndroidManifest.xml.
    int id = item.getItemId();

    //noinspection SimplifiableIfStatement
    if (id == R.id.action_settings) {
        return true;
    }

    return super.onOptionsItemSelected(item);
}

}

SNIPPET 2              

<uses-permission android:name="android.permission.INTERNET" />

<application
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:roundIcon="@mipmap/ic_launcher_round"
    android:supportsRtl="true"
    android:theme="@style/AppTheme">
    <activity
        android:name=".MainActivity"
        android:label="@string/app_name"
        android:theme="@style/AppTheme.NoActionBar">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />

            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
        <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="mycallback"
                android:scheme="myauth"/>
        </intent-filter>
    </activity>
</application>

P.S。:我从以下链接中的改进视频教程中受益,并尝试与教程中解释的内容几乎相同,但事情没有像教程中所解释的那样起作用。 Probabaly,我错过了一些设置,但谷歌搜索没有多大帮助。 视频教程链接:https://www.youtube.com/watch?time_continue=726&v=TnQUb-ACqWs

1 个答案:

答案 0 :(得分:1)

到目前为止你已经走上了正轨,但你可以通过添加一个单独的活动来改善代码处理,让我们称之为LoginActivity并设置android:launchMode="singleTop"(我将在稍后解释原因),如下所示:

<activity android:name=".view.LoginActivity"
        android:theme="@style/AppTheme.NoActionBar"
        android:exported="true"
        android:launchMode="singleTop" >
        <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="mycallback"
                android:scheme="myauth" />
        </intent-filter>
    </activity>

接下来在您的登录活动中进行以下更改提示注意onNewIntent(Intent)

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(this, R.layout.activity_login); 
    // R.layout is the name of your layout, what ever that will be, you may also want to check if the user is logged in or not before running the checkNewIntent carelessly, the logic of how to do this is up to you
    checkNewIntent(getIntent());        
}


@Override
protected void onNewIntent(Intent intent) {
    super.onNewIntent(intent);
    checkNewIntent(intent);
}


private void checkNewIntent(Intent intent) {
    if (intent != null && intent.getData() != null) {
        Uri intentUri = intent.getData();
        if(uri != null && uri.toString().startsWith(BuildConfig.REDIRECT_URI)) {
            String code = uri.getQueryParameter("code"); // do something with your code
        } else {
            // show an error toast
        }
    }
}

你可能想知道为什么我建议使用onNewIntent而不是onResume,当我开始使用oauth回调时,我遇到了类似的问题,除了Chrome之外的某些浏览器会失败调用我的活动响应我的回调而不提供任何数据意图,由于android系统如何使用默认启动模式调用现有活动。

使用singleTop将确保不会使用新意图重新创建现有活动,而是使用新意图将其带到前面,因此 onNewIntent()我希望我已经很好地解释了这一点期待您的回复。

还在 onCreate 中添加了 checkNewIntent 以解决特殊情况,例如,如果您的应用程序因任何原因在后台被杀,如果用户授予您的应用程序<如果活动被破坏,可能无法调用strong> onNewIntent ,至少从我的经验来看,我确定有人可能对此有更好的解释:)