我一直试图在我的Android应用中实施Google登录几周。我能够为Android应用程序执行登录操作,完美地获取访问令牌 但是,我有一个基于python的应用服务器,需要离线访问用户的谷歌帐户。我理解,为此我需要获取OAuth代码并将其发送到服务器。然后,服务器将使用OAuth代码获取刷新令牌 但是,这似乎不起作用。服务器永远无法获取刷新令牌。我经常收到错误,
invalid_client - 找不到OAuth客户端
这是android GooglePlusFragment.java,
package com.mywash.onboard;
import android.app.Activity;
import android.app.Fragment;
import android.content.Intent;
import android.content.IntentSender;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.crashlytics.android.Crashlytics;
import com.google.android.gms.auth.GoogleAuthUtil;
import com.google.android.gms.auth.UserRecoverableAuthException;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.Scopes;
import com.google.android.gms.common.SignInButton;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.common.api.ResultCallback;
import com.google.android.gms.common.api.Scope;
import com.google.android.gms.common.api.Status;
import com.google.android.gms.plus.Plus;
import com.google.android.gms.plus.model.people.Person;
import com.mywash.R;
import com.mywash.database.CurrentUserDBAdapter;
import com.mywash.server.ServerAsyncTask;
import com.mywash.server.ServerUrls;
import org.apache.http.NameValuePair;
import org.apache.http.message.BasicNameValuePair;
import java.util.ArrayList;
import java.util.Set;
public class GooglePlusFragment extends Fragment implements
GoogleApiClient.ConnectionCallbacks,
GoogleApiClient.OnConnectionFailedListener,
GoogleApiClient.ServerAuthCodeCallbacks
{
private final String TAG = "GooglePlusFragment";
/* Request code used to invoke sign in existingUser interactions. */
public static final int REQUEST_SIGNIN = 100;
public static final int REQUEST_AUTHORIZATION = 200;
public static final int REQUEST_AUTH_CODE = 300;
GooglePlusFragment instance = this;
/* Client used to interact with Google APIs. */
private static GoogleApiClient mGoogleApiClient;
private UserBean existingUser = null;
private String phoneNumber = "";
private boolean isFirstLogin = false;
/* A flag indicating that a PendingIntent is in progress and prevents
* us from starting further intents.
*/
private boolean mIntentInProgress;
/* Track whether the sign-in button has been clicked so that we know to resolve
* all issues preventing sign-in without waiting.
*/
private boolean mSignInClicked;
/* Store the connection result from onConnectionFailed callbacks so that we can
* resolve them when the existingUser clicks sign-in.
*/
private ConnectionResult mConnectionResult;
private OnFragmentInteractionListener mListener;
private Activity activity;
/**
* Use this factory method to create a new instance of
* this fragment using the provided parameters.
*
* @return A new instance of fragment GooglePlusFragment.
*/
public static GooglePlusFragment newInstance()
{
GooglePlusFragment fragment = new GooglePlusFragment();
return fragment;
}
public GooglePlusFragment()
{
// Required empty public constructor
}
@Override
public void onAttach(Activity activity)
{
super.onAttach(activity);
this.activity = activity;
try
{
mListener = (OnFragmentInteractionListener) activity;
}
catch (ClassCastException e)
{
Crashlytics.logException(e);
throw new ClassCastException(activity.toString()
+ " must implement OnFragmentInteractionListener");
}
}
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
try
{
CurrentUserDBAdapter userDBA = new CurrentUserDBAdapter(getActivity());
userDBA.open();
existingUser = userDBA.getUser();
phoneNumber = existingUser.getProfilePhone();
userDBA.close();
Log.d(TAG, "existingUser.isGooglePlusLogin() " + existingUser.isGooglePlusLogin());
Log.d(TAG, "existingUser.getProfileEmail() "+existingUser.getProfileEmail());
}
catch (Exception e)
{
e.printStackTrace();
Crashlytics.logException(e);
}
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
Log.d(TAG, "onCreateView");
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_google_plus, container, false);
mGoogleApiClient = new GoogleApiClient.Builder(getActivity())
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(Plus.API)
.addScope(Plus.SCOPE_PLUS_LOGIN)
.addScope(Plus.SCOPE_PLUS_PROFILE)
.build();
SignInButton signInButton = (SignInButton) view.findViewById(R.id.google_plus_login_button);
//signInButton.setVisibility(View.GONE); // Temp code.
signInButton.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View view)
{
Log.d(TAG, "mSignInClicked");
if (view.getId() == R.id.google_plus_login_button && !mGoogleApiClient.isConnecting())
{
mSignInClicked = true;
mGoogleApiClient.connect();
Log.d(TAG, "isConnecting "+mGoogleApiClient.isConnecting());
}
}
});
return view;
}
@Override
public void onStart()
{
super.onStart();
if(existingUser.isActive())
mGoogleApiClient.connect();
else
googlePlusLogout();
}
@Override
public void onStop()
{
super.onStop();
if (mGoogleApiClient.isConnected())
{
mGoogleApiClient.disconnect();
}
}
@Override
public void onDetach()
{
super.onDetach();
mListener = null;
}
@Override
public void onConnected(Bundle connectionHint)
{
try
{
Log.d(TAG, "#### onConnected");
if(mSignInClicked) {
mSignInClicked = false;
if (Plus.PeopleApi.getCurrentPerson(mGoogleApiClient) != null) {
Person person = Plus.PeopleApi.getCurrentPerson(mGoogleApiClient);
UserBean freshUserData = new UserBean();
freshUserData.setProfileName(person.getDisplayName());
freshUserData.setProfilePicUrl(person.getImage().getUrl());
freshUserData.setProfileEmail(Plus.AccountApi.getAccountName(mGoogleApiClient));
freshUserData.setGooglePlusLogin(true);
if (phoneNumber.length() > 0)
freshUserData.setProfilePhone(phoneNumber);
new LoginActivity.BitmapFromUrl().execute(freshUserData.getProfilePicUrl());
CurrentUserDBAdapter userDBA = new CurrentUserDBAdapter(getActivity());
userDBA.open();
userDBA.insertOrUpdateUser(freshUserData);
userDBA.close();
new AccessTokenTask().execute();
}
}
else
googlePlusLogout();
}
catch (Exception e)
{
e.printStackTrace();
Crashlytics.logException(e);
}
}
public void onConnectionFailed(ConnectionResult result)
{
if (!mIntentInProgress)
{
if (mSignInClicked && result.hasResolution())
{
// The existingUser has already clicked 'sign-in' so we attempt to resolve all
// errors until the existingUser is signed in, or they cancel.
try
{
result.startResolutionForResult(getActivity(), REQUEST_SIGNIN);
mIntentInProgress = true;
}
catch (IntentSender.SendIntentException e)
{
Crashlytics.logException(e);
// The intent was canceled before it was sent. Return to the default
// state and attempt to connect to get an updated ConnectionResult.
mIntentInProgress = false;
mGoogleApiClient.connect();
}
}
}
}
@Override
public void onConnectionSuspended(int i)
{
}
@Override
public CheckResult onCheckServerAuthorization(String s, Set<Scope> set) {
return null;
}
@Override
public boolean onUploadServerAuthCode(String idToken, String serverAuthCode)
{
return true;
}
private class AccessTokenTask extends AsyncTask<Void, Void, String>
{
String freshEmail = "";
@Override
protected void onPreExecute()
{
super.onPreExecute();
Log.d(TAG, "existingUser.getProfileEmail() >>"+existingUser.getProfileEmail() +"<<");
Log.d(TAG, "Plus.AccountApi.getAccountName(mGoogleApiClient) >>"+Plus.AccountApi.getAccountName(mGoogleApiClient)+"<<");
// if existing email id does not match with returned mail id, consider as first login.
if(!existingUser.getProfileEmail().equals(Plus.AccountApi.getAccountName(mGoogleApiClient)))
isFirstLogin = true;
}
@Override
protected String doInBackground(Void... params)
{
Log.d(TAG, "Fetching .. GOOGLE ACCESS TOKEN");
String accessToken = null;
try
{
freshEmail = Plus.AccountApi.getAccountName(mGoogleApiClient);
Log.d(TAG, "freshEmail .. "+freshEmail);
Log.d(TAG, "isFirstLogin .. "+isFirstLogin);
/*if(isFirstLogin)
{*/
Bundle appActivities = new Bundle();
appActivities.putString(GoogleAuthUtil.KEY_REQUEST_VISIBLE_ACTIVITIES,
"http://schema.org/AddAction");
String serverID = getString(R.string.GOOGLE_SERVICE_CLIENT_ID);
String scopes = "oauth2:server:client_id:" + serverID
+ ":api_scope: "
+ "https://www.googleapis.com/auth/plus.login" + " "
+ "https://www.googleapis.com/auth/plus.profile.emails.read" + " "
+ "https://www.googleapis.com/auth/userinfo.email" + " "
+ "https://www.googleapis.com/auth/userinfo.profile";
accessToken = GoogleAuthUtil.getToken(
getActivity(), // Context context
freshEmail, // String accountName
scopes, // String scope
appActivities); // Bundle bundle
mGoogleApiClient = new GoogleApiClient.Builder(getActivity())
// other builder methods
.addApi(Plus.API)
.addScope(Plus.SCOPE_PLUS_LOGIN)
.addScope(Plus.SCOPE_PLUS_PROFILE)
.requestServerAuthCode(serverID, instance)
.build();
/*}
else
{
String scope = "oauth2: " + Scopes.PLUS_LOGIN + " " + Scopes.PROFILE + " " + Scopes.PLUS_ME;
accessToken = GoogleAuthUtil.getToken(activity, freshEmail, scope);
}*/
}
catch (UserRecoverableAuthException e)
{
Crashlytics.logException(e);
startActivityForResult(e.getIntent(), REQUEST_AUTHORIZATION);
}
catch (Exception e)
{
Crashlytics.logException(e);
e.printStackTrace();
}
return accessToken;
}
@Override
protected void onPostExecute(String token)
{
super.onPostExecute(token);
Log.d(TAG, "GOOGLE ACCESS TOKEN: " + token);
try
{
if(token != null && !token.equals(""))
{
ArrayList<NameValuePair> nameValuePairs = new ArrayList<>();
nameValuePairs.add(new BasicNameValuePair("type", "android"));
nameValuePairs.add(new BasicNameValuePair("device_id", "blah_blah"));
nameValuePairs.add(new BasicNameValuePair("email", freshEmail));
nameValuePairs.add(new BasicNameValuePair("access_token", token));
ServerAsyncTask serverTask = new ServerAsyncTask(getActivity());
serverTask.setNameValuePairList(nameValuePairs);
serverTask.setCancellable(false);
serverTask.execute(ServerAsyncTask.POST, ServerUrls.POST_LOGIN, "0");
}
}
catch (Exception e)
{
e.printStackTrace();
Crashlytics.logException(e);
}
}
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data)
{
if (requestCode == REQUEST_SIGNIN)
{
if (resultCode != getActivity().RESULT_OK)
{
mSignInClicked = false;
}
mIntentInProgress = false;
if (!mGoogleApiClient.isConnected())
{
mGoogleApiClient.reconnect();
}
}
else if(requestCode == REQUEST_AUTHORIZATION)
{
new AccessTokenTask().execute();
}
}
public static void googlePlusLogout()
{
if (mGoogleApiClient.isConnected())
{
Plus.AccountApi.clearDefaultAccount(mGoogleApiClient);
Plus.AccountApi.revokeAccessAndDisconnect(mGoogleApiClient)
.setResultCallback(new ResultCallback<Status>() {
@Override
public void onResult(Status status) {
}
});
mGoogleApiClient.disconnect();
}
}
/**
* This interface must be implemented by activities that contain this
* fragment to allow an interaction in this fragment to be communicated
* to the activity and potentially other fragments contained in that
* activity.
* <p/>
* See the Android Training lesson <a href=
* "http://developer.android.com/training/basics/fragments/communicating.html"
* >Communicating with Other Fragments</a> for more information.
*/
public interface OnFragmentInteractionListener
{
// TODO: Update argument type and name
public void onGooglePlusFragmentInteraction(Uri uri);
}
}
这是发生错误的应用服务器python代码,
def get_android_data(self, access_token, email=None):
print access_token, email, "..........."
gplus_auth = False
if email:
try:
user = db.users.find_one({'email': email})
if user and 'android_credentials' in user:
print "creds found"
credentials = Credentials.new_from_json(json.dumps(user['android_credentials']))
user_info_service = build(
serviceName='oauth2',
version='v2',
http=credentials.authorize(httplib2.Http())
)
user_info = user_info_service.userinfo().get().execute()
else:
gplus_auth = True
except AccessTokenRefreshError, e:
gplus_auth = True
print e
if gplus_auth:
try:
oauth_flow = OAuth2WebServerFlow(
client_id=app.config['GPLUS_CREDS']['client_id'],
client_secret=app.config['GPLUS_CREDS']['client_secret'],
scope=app.config['GPLUS_CREDS']['scope'],
redirect_uri=app.config['GPLUS_CREDS']['redirect_uri']
)
credentials = oauth_flow.step2_exchange(access_token)
user_info_service = build(
serviceName='oauth2',
version='v2',
http=credentials.authorize(httplib2.Http())
)
user_info = user_info_service.userinfo().get().execute()
except FlowExchangeError, e:
print e
return {'status': 'failure', 'error': str(e)}, 403
insert_data = {}
insert_data['android_credentials'] = json.loads(credentials.to_json())
insert_data["pictureUrl"] = user_info.get('picture', "")
insert_data["user_id"] = "g_" + user_info.get("id", "")
insert_data["email"] = user_info.get("email", "")
insert_data["name"] = user_info.get("name", "") + user_info.get("givenName", "")+" "+user_info.get("name", "") + user_info.get("familyName", "")
insert_data['authData'] = {
'id': user_info.get("id", ""),
'type': 'google'
}
if self.user_exists(insert_data.get("user_id")):
insert_data["updatedAt"] = datetime.datetime.now()
device_id = set(db.users.find_one({'user_id': insert_data.get("user_id")}).get('device_id', []))
if device_id:
device_id.add(self.device_id)
insert_data['device_id'] = list(device_id)
result = db.users.update(
{'user_id': insert_data.get("user_id")},
{'$set': insert_data}
)
else:
insert_data['device_id'] = [self.device_id]
db.users.insert(insert_data)
emails.email_welcome({
"email": insert_data["email"],
"name": insert_data["name"]
})
print credentials.to_json()
print user_info
return {'status': 'success', 'id': insert_data['user_id']}
答案 0 :(得分:1)
问题似乎与您使用代码时使用的Google客户端凭据有关。您似乎已通过在 Google Developer Console-&gt;凭据 - >创建新客户ID
上选择“已安装的应用程序”为您的两个应用生成了一组凭据您需要通过选择 Google Developer Console-&gt;凭据 - >创建新客户ID <上的“Web应用程序”,从Google Developer Console生成另一组Python服务器应用凭据/ p>
这就是为什么API抱怨它是“invalid_client”向Google服务器发出请求的原因。希望这可以解决您的问题。