gpt4 book ai didi

android - 无法在 Cross Client google oauth2.0 中交换访问 token 和刷新 token 的授权码

转载 作者:太空狗 更新时间:2023-10-29 15:00:01 28 4
gpt4 key购买 nike

我在我的 Android 应用程序上实现 Google Play 服务登录并将授权代码传递到我的后端服务器时遇到问题,因此服务器将交换访问 token 和刷新 token 的代码。

首先让我写几行已经尝试/阅读的内容:

在 code.google.com/apis/console 我创建了一个新项目,有两个客户端(WEB 客户端和 Android 安装客户端)阅读有关 https://developers.google.com/+/mobile/android/sign-in#cross-platform_single_sign_on 的文章和 http://android-developers.blogspot.com/2013/01/verifying-back-end-calls-from-android.html

这是我的客户端代码,用于检索授权代码和 IdToken:

    package com.google.drive.samples.crossclientoauth2;

import java.util.Arrays;
import java.util.List;

import android.accounts.AccountManager;
import android.app.Activity;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Log;
import android.widget.EditText;

import com.google.android.gms.auth.GoogleAuthUtil;
import com.google.android.gms.auth.UserRecoverableAuthException;
import com.google.api.client.googleapis.extensions.android.gms.auth.GoogleAccountCredential;
import com.google.api.client.googleapis.extensions.android.gms.auth.UserRecoverableAuthIOException;

public class MainActivity extends Activity {

final private String CLIENT_ID = MY WEB SERVER'S CLIENT ID;
final private List<String> SCOPES = Arrays.asList(new String[]{
"https://www.googleapis.com/auth/plus.login",
"https://www.googleapis.com/auth/drive",
"https://www.googleapis.com/auth/youtube",
"https://www.googleapis.com/auth/youtube.readonly"
});

// I have modified the above line of code.

private GoogleAccountCredential mCredential;

private EditText mExchangeCodeEditText;
private EditText mIdTokenEditText;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);
mExchangeCodeEditText = (EditText) findViewById(R.id.editTextExchangeCode);
mIdTokenEditText = (EditText) findViewById(R.id.editTextIdToken);

// initiate a credential object with drive and plus.login scopes
// cross identity is only available for tokens retrieved with plus.login
mCredential = GoogleAccountCredential.usingOAuth2(this, null);

// user needs to select an account, start account picker
startActivityForResult(
mCredential.newChooseAccountIntent(), REQUEST_ACCOUNT_PICKER);

}

/**
* Handles the callbacks from result returning
* account picker and permission requester activities.
*/
@Override
protected void onActivityResult(
final int requestCode, final int resultCode, final Intent data) {
switch (requestCode) {
// user has returned back from the account picker,
// initiate the rest of the flow with the account he/she has chosen.
case REQUEST_ACCOUNT_PICKER:
String accountName = data.getStringExtra(AccountManager.KEY_ACCOUNT_NAME);
if (accountName != null) {
mCredential.setSelectedAccountName(accountName);
new RetrieveExchangeCodeAsyncTask().execute();
new RetrieveJwtAsyncTask().execute();
}
break;
// user has returned back from the permissions screen,
// if he/she has given enough permissions, retry the the request.
case REQUEST_AUTHORIZATION:
if (resultCode == Activity.RESULT_OK) {
// replay the same operations
new RetrieveExchangeCodeAsyncTask().execute();
new RetrieveJwtAsyncTask().execute();
}
break;
}
}

/**
* Retrieves the exchange code to be sent to the
* server-side component of the app.
*/

public class RetrieveExchangeCodeAsyncTask
extends AsyncTask<Void, Boolean, String> {


@Override
protected String doInBackground(Void... params) {
String scope = String.format("oauth2:server:client_id:%s:api_scope:%s",
CLIENT_ID, TextUtils.join(" ", SCOPES));
try {
return GoogleAuthUtil.getToken(
MainActivity.this, mCredential.getSelectedAccountName(), scope);
} catch (UserRecoverableAuthException e) {
startActivityForResult(e.getIntent(), REQUEST_AUTHORIZATION);
} catch (Exception e) {
e.printStackTrace(); // TODO: handle the exception
}
return null;
}

@Override
protected void onPostExecute(String code) {
// exchange code with server-side to retrieve an additional
// access token on the server-side.
Log.v("first One ","code 1 is: "+ code);
mExchangeCodeEditText.setText(code);
}
}

/**
* Retrieves a JWT to identify the user without the
* regular client-side authorization flow. The jwt payload needs to be
* sent to the server-side component.
*/
public class RetrieveJwtAsyncTask
extends AsyncTask<Void, Boolean, String> {

@Override
protected String doInBackground(Void... params) {
String scope = "audience:server:client_id:" + CLIENT_ID;
try {
return GoogleAuthUtil.getToken(
MainActivity.this, mCredential.getSelectedAccountName(), scope);
} catch(UserRecoverableAuthIOException e) {
startActivityForResult(e.getIntent(), REQUEST_AUTHORIZATION);
} catch (Exception e) {
e.printStackTrace(); // TODO: handle the exception
}
return null;
}

@Override
protected void onPostExecute(String idToken) {
// exchange encrypted idToken with server-side to identify the user
Log.v("Second One","2222"+ idToken);
mIdTokenEditText.setText(idToken);
}
}

private static final int REQUEST_ACCOUNT_PICKER = 100;
private static final int REQUEST_AUTHORIZATION = 200;

}

上面的代码给了我两个代码:1.一个由 RetrieveExchangeCodeAsyncTask 返回的命名代码。2.第二个由 RetrieveJwtAsyncTask 类返回,名为 IdToken。

首先,我很困惑我需要将上面的哪一个发送到我的网络服务器,在那里将进行交换。我尝试使用第一个(以“4/....”开头的那个)在我的服务器端进行交换,但出现空指针异常。还请指定我需要使用的重定向 URI。

这是我用于交换的服务器端代码:

package com.myAuthSample.tial;

import java.io.IOException;

import com.myAuthSample.tial.MyClass.CodeExchangeException;
import com.myAuthSample.tial.MyClass.NoRefreshTokenException;

public class MyMainDemo {

public static void main(String[] args) {
// TODO Auto-generated method stub
try {
MyClass.getCredentials("4/...something...", "state"); //passed the retrieved authorization code
} catch (CodeExchangeException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NoRefreshTokenException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

}


public class MyClass {


// Path to client_secrets.json which should contain a JSON document such as:
// {
// "web": {
// "client_id": "[[YOUR_CLIENT_ID]]",
// "client_secret": "[[YOUR_CLIENT_SECRET]]",
// "auth_uri": "https://accounts.google.com/o/oauth2/auth",
// "token_uri": "https://accounts.google.com/o/oauth2/token"
// }
// }
private static final String CLIENTSECRETS_LOCATION = "/client_secrets_2.json";// client secrets of my android client

// private static final String REDIRECT_URI = "<YOUR_REGISTERED_REDIRECT_URI>";

private static String REDIRECT_URI ="";



private static final List<String> SCOPES = Arrays.asList(
"https://www.googleapis.com/auth/plus.login",
"https://www.googleapis.com/auth/youtube");

private static GoogleAuthorizationCodeFlow flow = null;

/**
* Exception thrown when an error occurred while retrieving credentials.
*/
public static class GetCredentialsException extends Exception {

protected String authorizationUrl;

/**
* Construct a GetCredentialsException.
*
* @param authorizationUrl The authorization URL to redirect the user to.
*/
public GetCredentialsException(String authorizationUrl) {
this.authorizationUrl = authorizationUrl;
}

/**
* Set the authorization URL.
*/
public void setAuthorizationUrl(String authorizationUrl) {
this.authorizationUrl = authorizationUrl;
}

/**
* @return the authorizationUrl
*/
public String getAuthorizationUrl() {
return authorizationUrl;
}
}

/**
* Exception thrown when a code exchange has failed.
*/
public static class CodeExchangeException extends GetCredentialsException {

/**
* Construct a CodeExchangeException.
*
* @param authorizationUrl The authorization URL to redirect the user to.
*/
public CodeExchangeException(String authorizationUrl) {
super(authorizationUrl);
}

}

/**
* Exception thrown when no refresh token has been found.
*/
public static class NoRefreshTokenException extends GetCredentialsException {

/**
* Construct a NoRefreshTokenException.
*
* @param authorizationUrl The authorization URL to redirect the user to.
*/
public NoRefreshTokenException(String authorizationUrl) {
super(authorizationUrl);
}

}

/**
* Exception thrown when no user ID could be retrieved.
*/
private static class NoUserIdException extends Exception {
}

/**
* Retrieved stored credentials for the provided user ID.
*
* @param userId User's ID.
* @return Stored Credential if found, {@code null} otherwise.
*/
static Credential getStoredCredentials(String userId) {
// TODO: Implement this method to work with your database. Instantiate a new
// Credential instance with stored accessToken and refreshToken.
throw new UnsupportedOperationException();
}

/**
* Store OAuth 2.0 credentials in the application's database.
*
* @param userId User's ID.
* @param credentials The OAuth 2.0 credentials to store.
*/
static void storeCredentials(String userId, Credential credentials) {
// TODO: Implement this method to work with your database.
// Store the credentials.getAccessToken() and credentials.getRefreshToken()
// string values in your database.
System.out.println("credentials are : " + credentials.toString());
throw new UnsupportedOperationException();
}

/**
* Build an authorization flow and store it as a static class attribute.
*
* @return GoogleAuthorizationCodeFlow instance.
* @throws IOException Unable to load client_secrets.json.
*/
static GoogleAuthorizationCodeFlow getFlow() throws IOException {
if (flow == null) {
HttpTransport httpTransport = new NetHttpTransport();
JacksonFactory jsonFactory = new JacksonFactory(); //...this was the original line....
// JsonFactory jsonFactory = new JacksonFactory();

//my code....
Reader clientSecretReader = new InputStreamReader(MyClass.class.getResourceAsStream(CLIENTSECRETS_LOCATION));

GoogleClientSecrets clientSecrets =
GoogleClientSecrets.load(jsonFactory,clientSecretReader);

REDIRECT_URI =clientSecrets.getDetails().getRedirectUris().get(0);
// my code ends...

/* GoogleClientSecrets clientSecrets =
GoogleClientSecrets.load(jsonFactory,
MyClass.class.getResourceAsStream(CLIENTSECRETS_LOCATION));
*/
flow =
new GoogleAuthorizationCodeFlow.Builder(httpTransport, jsonFactory, clientSecrets, SCOPES)
.setAccessType("offline").setApprovalPrompt("force").build();
}
return flow;
}

/**
* Exchange an authorization code for OAuth 2.0 credentials.
*
* @param authorizationCode Authorization code to exchange for OAuth 2.0
* credentials.
* @return OAuth 2.0 credentials.
* @throws CodeExchangeException An error occurred.
*/
static Credential exchangeCode(String authorizationCode)
throws CodeExchangeException {
try {
GoogleAuthorizationCodeFlow flow = getFlow();
GoogleTokenResponse response =
flow.newTokenRequest(authorizationCode).setRedirectUri(REDIRECT_URI).execute();
return flow.createAndStoreCredential(response, null);
} catch (IOException e) {
System.err.println("An error occurred: " + e);
throw new CodeExchangeException(null);
}
}

/**
* Send a request to the UserInfo API to retrieve the user's information.
*
* @param credentials OAuth 2.0 credentials to authorize the request.
* @return User's information.
* @throws NoUserIdException An error occurred.
*/
static Userinfo getUserInfo(Credential credentials)
throws NoUserIdException {
Oauth2 userInfoService =
new Oauth2.Builder(new NetHttpTransport(), new JacksonFactory(), credentials).build();
Userinfo userInfo = null;
try {
userInfo = userInfoService.userinfo().get().execute();
} catch (IOException e) {
System.err.println("An error occurred: " + e);
}
if (userInfo != null && userInfo.getId() != null) {
return userInfo;
} else {
throw new NoUserIdException();
}
}

/**
* Retrieve the authorization URL.
*
* @param emailAddress User's e-mail address.
* @param state State for the authorization URL.
* @return Authorization URL to redirect the user to.
* @throws IOException Unable to load client_secrets.json.
*/
public static String getAuthorizationUrl(String emailAddress, String state) throws IOException {
GoogleAuthorizationCodeRequestUrl urlBuilder =
getFlow().newAuthorizationUrl().setRedirectUri(REDIRECT_URI).setState(state);
urlBuilder.set("user_id", emailAddress);
return urlBuilder.build();
}

/**
* Retrieve credentials using the provided authorization code.
*
* This function exchanges the authorization code for an access token and
* queries the UserInfo API to retrieve the user's e-mail address. If a
* refresh token has been retrieved along with an access token, it is stored
* in the application database using the user's e-mail address as key. If no
* refresh token has been retrieved, the function checks in the application
* database for one and returns it if found or throws a NoRefreshTokenException
* with the authorization URL to redirect the user to.
*
* @param authorizationCode Authorization code to use to retrieve an access
* token.
* @param state State to set to the authorization URL in case of error.
* @return OAuth 2.0 credentials instance containing an access and refresh
* token.
* @throws NoRefreshTokenException No refresh token could be retrieved from
* the available sources.
* @throws IOException Unable to load client_secrets.json.
*/
public static Credential getCredentials(String authorizationCode, String state)
throws CodeExchangeException, NoRefreshTokenException, IOException {
String emailAddress = "";
try {
Credential credentials = exchangeCode(authorizationCode);
Userinfo userInfo = getUserInfo(credentials);
String userId = userInfo.getId();
emailAddress = userInfo.getEmail();
if (credentials.getRefreshToken() != null) {
storeCredentials(userId, credentials);
return credentials;
} else {
credentials = getStoredCredentials(userId);
if (credentials != null && credentials.getRefreshToken() != null) {
return credentials;
}
}
} catch (CodeExchangeException e) {
e.printStackTrace();
// Drive apps should try to retrieve the user and credentials for the current
// session.
// If none is available, redirect the user to the authorization URL.
e.setAuthorizationUrl(getAuthorizationUrl(emailAddress, state));
throw e;
} catch (NoUserIdException e) {
e.printStackTrace();
}
// No refresh token has been retrieved.
String authorizationUrl = getAuthorizationUrl(emailAddress, state);
throw new NoRefreshTokenException(authorizationUrl);
}


}

此外,我是否在我的服务器端代码(在 MyClass 中)传递了正确的 client_secret.json 文件——这是 android 客户端。

求助!!!提前致谢。

最佳答案

从这里使用:

import com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeTokenRequest;
import com.google.api.client.googleapis.auth.oauth2.GoogleIdToken;
import com.google.api.client.googleapis.auth.oauth2.GoogleTokenResponse;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.json.jackson.JacksonFactory;


private final val TRANSPORT: HttpTransport = new NetHttpTransport()
private final val JSON_FACTORY: JacksonFactory = new JacksonFactory()


GoogleTokenResponse tokenResponse = new GoogleAuthorizationCodeTokenRequest(TRANSPORT, JSON_FACTORY,
CLIENT_ID, CLIENT_SECRET, code, "postmessage").execute();

GoogleIdToken idToken = tokenResponse.parseIdToken();
String gplusId = idToken.getPayload().getSubject();

您必须用上述相关变量替换您的 client_id、client_secret 和代码的值。

更多信息在流动链接中: https://github.com/googleplus/gplus-quickstart-java/blob/master/src/com/google/plus/samples/quickstart/Signin.java

您还可以从此链接获取 Google API 库:

http://repo1.maven.org/maven2/com/google/

关于android - 无法在 Cross Client google oauth2.0 中交换访问 token 和刷新 token 的授权码,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27693204/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com