![](/img/trans.png)
[英]Difference between GoogleSignInApi and PLUS API for Android
[英]perform google sheets api access with android GoogleSignInApi credentials
我知道有几个帖子讨论了 android 的登录/oauth,但没有一个能解决我的问题。
我正在努力使用 Google 表格 v4 教程中的 AccountManager 来协调身份验证流程与 GoogleSignInApi。
使用 GoogleSignInApi 我最终得到了一个授权码。 到目前为止一切顺利。 接下来,文档建议将 authcode 交换为 authtoken/recovery 令牌。 https://developers.google.com/identity/sign-in/android/offline-access有一个很好的例子,说明如何将身份验证代码发送到后端进行交换。
此流程的唯一问题 - 我没有自己的后端,因为我只想访问 google sheet api。 sheet api 调用需要一个 GoogleCredential 对象,我无法从 authcode 或以其他方式通过 GoogleSignInAccount 对象获取该对象。
所以,我的问题:
这是我正在尝试制作的 sheet api 调用的简化版本。
GoogleCredential credential = new GoogleCredential().setAccessToken("TEST_ACCESS_TOKEN_FROM_OAUTH_PLAYGROUND");
mService = new com.google.api.services.sheets.v4.Sheets.Builder(
transport, jsonFactory, credential)
.setApplicationName("Google Sheets API Android Quickstart")
.build();
更新:为了取得一些进展,我最终实现了一个服务器端流程来交换令牌。 我很确定,这不是正确的技术,因为它需要在应用程序中使用 client_secret。
第 1 部分:SignInActivity 基于 firebase 代码实验室。 我需要一个 firebase 帐户,所以我觉得我必须使用 GoogleSignInApi。
public class SignInActivity extends AppCompatActivity implements GoogleApiClient.OnConnectionFailedListener,
View.OnClickListener {
private static final int RC_SIGN_IN = 9001;
private GoogleApiClient mGoogleApiClient;
private FirebaseAuth mFirebaseAuth;
public static final String PREF_ACCOUNT_NAME = "accountName";
public static final String PREF_ID_TOKEN = "idToken";
public static final String PREF_AUTH_CODE = "authCode";
public static final Scope SHEETS_SCOPE = new Scope(SheetsScopes.SPREADSHEETS);
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_sign_in);
SignInButton signInButton = (SignInButton) findViewById(R.id.sign_in_button);
signInButton.setOnClickListener(this);
Log.d(TAG, getString(R.string.default_web_client_id));
GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestIdToken(getString(R.string.default_web_client_id))
.requestScopes(SHEETS_SCOPE)
.requestServerAuthCode(getString(R.string.default_web_client_id))
.requestEmail()
.build();
mGoogleApiClient = new GoogleApiClient.Builder(this)
.enableAutoManage(this /* FragmentActivity */, this /* OnConnectionFailedListener */)
.addApi(Auth.GOOGLE_SIGN_IN_API, gso)
.build();
// Initialize FirebaseAuth
mFirebaseAuth = FirebaseAuth.getInstance();
}
private void handleFirebaseAuthResult(AuthResult authResult) {
// ...
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.sign_in_button:
signIn();
break;
default:
return;
}
}
private void signIn() {
Intent signInIntent = Auth.GoogleSignInApi.getSignInIntent(mGoogleApiClient);
startActivityForResult(signInIntent, RC_SIGN_IN);
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
// Result returned from launching the Intent from GoogleSignInApi.getSignInIntent(...);
if (requestCode == RC_SIGN_IN) {
GoogleSignInResult result = Auth.GoogleSignInApi.getSignInResultFromIntent(data);
if (result.isSuccess()) {
// Google Sign In was successful, authenticate with Firebase
GoogleSignInAccount account = result.getSignInAccount();
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this.getApplicationContext());
SharedPreferences.Editor editor = prefs.edit();
editor.putString(PREF_ACCOUNT_NAME, account.getEmail());
editor.putString(PREF_ID_TOKEN, account.getIdToken());
editor.putString(PREF_AUTH_CODE, account.getServerAuthCode());
editor.apply();
// TODO: it would be great to do the exchange of the authcode now but it's doing a
// network call and can't be on the main thread.
// I really need this one
firebaseAuthWithGoogle(account);
} else {
// Google Sign In failed
Log.e(TAG, "Google Sign In failed.");
}
}
}
private void firebaseAuthWithGoogle(GoogleSignInAccount acct) {
Log.d(TAG, "firebaseAuthWithGoogle:" + acct.getId());
AuthCredential credential = GoogleAuthProvider.getCredential(acct.getIdToken(), null);
mFirebaseAuth.signInWithCredential(credential)
.addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
@Override
public void onComplete(@NonNull Task<AuthResult> task) {
Log.d(TAG, "signInWithCredential:onComplete:" + task.isSuccessful());
// If sign in fails, display a message to the user. If sign in succeeds
// the auth state listener will be notified and logic to handle the
// signed in user can be handled in the listener.
if (!task.isSuccessful()) {
Log.w(TAG, "signInWithCredential", task.getException());
Toast.makeText(SignInActivity.this, "Authentication failed.",
Toast.LENGTH_SHORT).show();
} else {
startActivity(new Intent(SignInActivity.this, MainActivity.class));
finish();
}
}
});
}
@Override
public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
// An unresolvable error has occurred and Google APIs (including Sign-In) will not
// be available.
Log.d(TAG, "onConnectionFailed:" + connectionResult);
Toast.makeText(this, "Google Play Services error.", Toast.LENGTH_SHORT).show();
}
}
第 2 部分:DataManager 是一个实用程序类,应用程序使用它来访问工作表数据。 它不使用工作表代码实验室中推荐的流程,因为该流程不允许我使用相同的用户数据设置 firebase 帐户。
public class DataManager {
public static final String UNDEF = "undefined";
private com.google.api.services.sheets.v4.Sheets mService = null;
// this is the play copy
private static String mSheetID = SHEET_ID;
private static final String PREF_ACCESS_TOKEN = "accessToken";
private static final String PREF_REFRESH_TOKEN = "refreshToken";
private static final String PREF_EXPIRES_IN_SECONDS = "expiresInSec";
private Context mContext;
private String mAccessToken;
private String mRefreshToken;
private Long mExpiresInSeconds;
private String mAuthCode;
public DataManager(Context context) {
mContext = context;
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mContext);
mAuthCode = prefs.getString(SignInActivity.PREF_AUTH_CODE, UNDEF);
mAccessToken = prefs.getString(PREF_ACCESS_TOKEN, UNDEF);
mRefreshToken = prefs.getString(PREF_REFRESH_TOKEN, UNDEF);
mExpiresInSeconds = prefs.getLong(PREF_EXPIRES_IN_SECONDS, 0);
}
private void exchangeCodeForToken(String authCode) {
try {
GoogleTokenResponse tokenResponse =
new GoogleAuthorizationCodeTokenRequest(
new NetHttpTransport(),
JacksonFactory.getDefaultInstance(),
"https://www.googleapis.com/oauth2/v4/token",
mContext.getString(R.string.default_web_client_id),
// TODO: the app shouldn't have to use the client secret
{CLIENT_SECRET},
authCode,
"")
.execute();
mAccessToken = tokenResponse.getAccessToken();
mRefreshToken = tokenResponse.getRefreshToken();
mExpiresInSeconds = tokenResponse.getExpiresInSeconds();
// TODO: do I really need to store and pass the three values individually?
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mContext);
SharedPreferences.Editor editor = prefs.edit();
editor.putString(PREF_ACCESS_TOKEN, mAccessToken);
editor.putString(PREF_REFRESH_TOKEN, mRefreshToken);
editor.putLong(PREF_EXPIRES_IN_SECONDS, mExpiresInSeconds);
editor.remove(SignInActivity.PREF_AUTH_CODE);
editor.apply();
} catch (Exception e) {
Log.e(TAG, "Token exchange failed with " + e.getMessage());
}
}
private void refreshAccessToken(String refreshToken) {
try {
// TODO: what to do here?
throw new Exception("TBD");
} catch (Exception e) {
Log.e(TAG, "Token refresh failed with " + e.getMessage());
}
}
private GoogleCredential getCredential() {
if (mAuthCode != UNDEF) {
exchangeCodeForToken(mAuthCode);
}
// TODO: handle missing or expired token
if (mRefreshToken != UNDEF && mExpiresInSeconds < 30) {
refreshAccessToken(mRefreshToken);
}
GoogleCredential credential = new GoogleCredential.Builder()
.setTransport(new NetHttpTransport())
.setJsonFactory(JacksonFactory.getDefaultInstance())
.build();
credential.setAccessToken(mAccessToken);
if (mRefreshToken != UNDEF) {
credential.setRefreshToken(mRefreshToken);
credential.setExpiresInSeconds(mExpiresInSeconds);
}
return credential;
}
// Set up credential and service object, then issue api call.
public ArrayList<Foo> getFooListFromServer() throws IOException {
try {
GoogleCredential credential = getCredential();
HttpTransport transport = AndroidHttp.newCompatibleTransport();
JsonFactory jsonFactory = JacksonFactory.getDefaultInstance();
mService = new com.google.api.services.sheets.v4.Sheets.Builder(
transport, jsonFactory, credential)
.setApplicationName(mContext.getString(R.string.app_name))
.build();
return getDataFromServer();
} catch (IOException exception) {
// ...
throw exception;
} catch (Exception e) {
Log.e(TAG, "something else is going on " + e.toString());
throw e;
}
}
/**
* Actually fetch the data from google
*
* @return List of Foos
* @throws IOException
*/
private ArrayList<Foo> getDataFromServer() throws IOException {
ArrayList<Foo> foos = new ArrayList<Foo>();
ValueRange response = this.mService.spreadsheets().values()
.get(mSheetID, mRange)
.setValueRenderOption("UNFORMATTED_VALUE")
.setDateTimeRenderOption("FORMATTED_STRING")
.execute();
//...
return foos;
}
}
如果您使用Android Quickstart for Sheets API ,您的凭据问题将很容易避免。
以下是指南中提到的步骤:
Step 1: Acquire a SHA1 fingerprint
Step 2: Turn on the Google Sheets API
Step 3: Create a new Android project
Step 4: Prepare the project
Step 5: Setup the sample
OAuth 客户端 ID 可在Google Dev Console 中找到。
我知道我对这个答案已经很晚了……但我只是想帮助大家。 经过 3 天的 RnD,终于我通过 google 登录 android 在 oAuth Google 中找到了 Refresh token is null 的工作解决方案
每个人都在使用这个:
GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestIdToken(getString(R.string.default_web_client_id))
.requestScopes(SHEETS_SCOPE)
.requestServerAuthCode(getString(R.string.default_web_client_id))
.requestEmail()
.build();
再添加一个参数:
GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestIdToken(getString(R.string.default_web_client_id))
.requestScopes(SHEETS_SCOPE)
.requestServerAuthCode(getString(R.string.default_web_client_id), true) //For offline access or background access token generation
.requestEmail()
.build();
在此之后,您将获得一个服务器身份验证令牌,它返回 refresh_token 和 access_token
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.