如何从 GoogleCredential 迁移到 GoogleCredentials 并仍然可以访问 People API?
How to migrate from GoogleCredential to GoogleCredentials and still get access to People API?
背景
对于我正在开发的应用程序,它使用人员 API 使用凭据(用户登录)。一旦用户提供凭据,我就可以访问各种 Google API,例如 People API。一个例子是获取联系人列表:
https://developers.google.com/people/api/rest/v1/people.connections/list
我注意到 class com.google.api.client.googleapis.auth.oauth2.GoogleCredential
已被弃用:
问题
该应用程序的旧代码基于一些旧的 G+ 代码 (here),可通过 Google 帐户联系联系人。这是其中最重要部分的一个片段,这让我很难从中迁移出来:
object GoogleOuthHelper {
@WorkerThread
fun setUp(context: Context, serverAuthCode: String?): Services {
val httpTransport: HttpTransport = NetHttpTransport()
val jsonFactory = JacksonFactory.getDefaultInstance()
// Redirect URL for web based applications. Can be empty too.
val redirectUrl = "urn:ietf:wg:oauth:2.0:oob"
// Exchange auth code for access token
val tokenResponse = GoogleAuthorizationCodeTokenRequest(
httpTransport, jsonFactory, GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET,
serverAuthCode, redirectUrl)
.execute()
// Then, create a GoogleCredential object using the tokens from GoogleTokenResponse
val credential = GoogleCredential.Builder()
.setClientSecrets(GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET)
.setTransport(httpTransport)
.setJsonFactory(jsonFactory)
.build()
val accessToken = tokenResponse.accessToken
getDefaultSecuredSharedPreferences(context).edit()
.putString(SecuredSharedPreferences.KEY__GOOGLE_ACCESS_TOKEN, accessToken).apply()
credential.setFromTokenResponse(tokenResponse)
val appPackageName = context.packageName
val peopleServiceApi = PeopleService.Builder(httpTransport, jsonFactory, credential)
.setApplicationName(appPackageName)
.build()
val peopleService = peopleServiceApi.people()
val otherContactsService = peopleServiceApi.otherContacts()
val contactGroups = peopleServiceApi.contactGroups()
return Services(peopleService, otherContactsService, contactGroups)
}
class Services(
/**https://developers.google.com/people/api/rest/v1/people*/
val peopleService: PeopleService.People,
/**https://developers.google.com/people/api/rest/v1/otherContacts*/
val otherContactsService: OtherContacts,
/**https://developers.google.com/people/api/rest/v1/contactGroups*/
val contactGroups: ContactGroups)
}
问题甚至从一开始就存在:
class GoogleCredentials
似乎不接受我在 GoogleCredential
class.
上面得到的任何东西
要添加更多内容,此函数将“serverAuthCode”作为参数,该参数来自 GoogleSignInAccount
,但要获取它,我需要使用已弃用的 GoogleApiClient
class :
fun prepareGoogleApiClient(someContext: Context): GoogleApiClient {
val context = someContext.applicationContext ?: someContext
val gso = GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestServerAuthCode(GOOGLE_CLIENT_ID)
.requestEmail()
.requestScopes(
Scope(PeopleServiceScopes.CONTACTS_READONLY),
Scope(PeopleServiceScopes.USERINFO_PROFILE),
Scope(PeopleServiceScopes.USER_EMAILS_READ),
Scope(PeopleServiceScopes.CONTACTS),
Scope(PeopleServiceScopes.CONTACTS_OTHER_READONLY)
)
.build()
return GoogleApiClient.Builder(context)
.addApi(Auth.GOOGLE_SIGN_IN_API, gso)
.build()
}
这就是我用它做的:
val connectionResult = googleApiClient!!.blockingConnect()
if (!connectionResult.isSuccess)
return
val operation = Auth.GoogleSignInApi.silentSignIn(googleApiClient)
val googleSignInResult: GoogleSignInResult = operation.await()
val googleSignInAccount = googleSignInResult.signInAccount
//use googleSignInAccount.serverAuthCode in setUp() function above
Gradle 文件具有以下依赖项:
// https://mvnrepository.com/artifact/com.google.auth/google-auth-library-oauth2-http
implementation 'com.google.auth:google-auth-library-oauth2-http:0.26.0'
// https://github.com/googleapis/google-api-java-client-services/tree/master/clients/google-api-services-people/v1#gradle https://mvnrepository.com/artifact/com.google.apis/google-api-services-people
implementation 'com.google.apis:google-api-services-people:v1-rev20210515-1.31.0'
我试过的
除了查看文档(并且没有看到与我必须处理的内容相似)之外,我尝试在此处写下这个。
很遗憾,我还找不到如何从旧代码中迁移出来的方法。
我试图在那里询问如何迁移 (here and here),但还没有得到答案。
问题
如何从 GoogleCredential
迁移到 GoogleCredentials
,同时仍然使用各种 API,例如人员 API?
换句话说:如何避免在 Android 上使用任何已弃用的 classes(GoogleCredential 和 GoogleApiClient),同时仍然能够使用各种 APIs?
How can I migrate away from GoogleCredential
to GoogleCredentials
while still using the various APIs such as People API?
In other words: How can I avoid using any of those deprecated classes (GoogleCredential and GoogleApiClient), on Android, while still being able to use the various APIs?
虽然您可以使 GoogleCredentials 直接工作,但最好使用从 GoogleCredentials 派生的 class,例如UserCredentials 将像 GoogleCredential 一样适应令牌刷新。 GoogleCredentials 更像是基础 class。
以下代码使用了 UserCredentials。这主要是您提供的内容,但为了演示的目的,我更改了一些凭证存储逻辑。此代码除了 startActivityForResult()
.
外没有弃用的方法
serverAuthCode
可从 GoogleSignInAccount 获得。查看 Moving Past GoogleApiClient on how to remove the dependency on GoogleApiClient. I have update my public gist of RestApiActivity from the Google signin quickstart,其中显示了如何使用 GoogleOauthHelper 以及 GoogleApi。
GoogleOauthHelper.kt
object GoogleOauthHelper {
@WorkerThread
fun setUp(context: Context, serverAuthCode: String?): Services {
val httpTransport: HttpTransport = NetHttpTransport()
val jsonFactory = GsonFactory.getDefaultInstance()
// Redirect URL for web based applications. Can be empty too.
val redirectUrl = "urn:ietf:wg:oauth:2.0:oob"
// Patch for demo
val GOOGLE_CLIENT_ID = context.getString(R.string.GOOGLE_CLIENT_ID)
val GOOGLE_CLIENT_SECRET = context.getString(R.string.GOOGLE_CLIENT_SECRET)
var accessToken: AccessToken? = null
var refreshToken =
getDefaultSecuredSharedPreferences(context).getString(
SecuredSharedPreferences.KEY_GOOGLE_REFRESH_TOKEN,
null
)
if (refreshToken == null) {
/* Did we lose the refresh token, or is this the first time? Refresh tokens are only
returned the first time after the user grants us permission to use the API. So, if
this is the first time doing this, we should get a refresh token. If it's not the
first time, we will not get a refresh token, so we will proceed with the access
token alone. If the access token expires (in about an hour), we will get an error.
What we should do is to ask the user to reauthorize the app and go through the
OAuth flow again to recover a refresh token.
See https://developers.google.com/identity/protocols/oauth2#expiration regarding
how a refresh token can become invalid.
*/
val tokenResponse = GoogleAuthorizationCodeTokenRequest(
httpTransport, jsonFactory, GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET,
serverAuthCode, redirectUrl
).execute()
refreshToken = tokenResponse.refreshToken
if (refreshToken != null) {
getDefaultSecuredSharedPreferences(context).edit()
.putString(SecuredSharedPreferences.KEY_GOOGLE_REFRESH_TOKEN, refreshToken)
.apply()
} else {
Log.d("Applog", "No refresh token. Going with access token alone.")
val expiresAtMilliseconds =
Clock.SYSTEM.currentTimeMillis() + tokenResponse.expiresInSeconds * 1000
accessToken = AccessToken(tokenResponse.accessToken, Date(expiresAtMilliseconds))
}
}
Log.d("Applog", "Refresh token: $refreshToken")
// UserCredentials extends GoogleCredentials and permits token refreshing.
val googleCredentials = UserCredentials.newBuilder().run {
clientId = GOOGLE_CLIENT_ID
clientSecret = GOOGLE_CLIENT_SECRET
setRefreshToken(refreshToken)
setAccessToken(accessToken)
build()
}
// Save access token on change
googleCredentials.addChangeListener { oAuth2Credentials ->
saveAccessToken(oAuth2Credentials.accessToken)
}
val requestInitializer: HttpRequestInitializer = HttpCredentialsAdapter(googleCredentials)
val appPackageName = context.packageName
val peopleServiceApi = PeopleService.Builder(httpTransport, jsonFactory, requestInitializer)
.setApplicationName(appPackageName)
.build()
return peopleServiceApi.run { Services(people(), otherContacts(), contactGroups()) }
}
private fun saveAccessToken(accessToken: AccessToken) {
// Persist the token securely.
Log.d("Applog", "Access token has changed: ${accessToken.tokenValue}")
}
// Warning insecure!: Patch for demo.
private fun getDefaultSecuredSharedPreferences(context: Context): SharedPreferences {
return PreferenceManager.getDefaultSharedPreferences(context)
}
// Warning insecure!: Patch for demo.
object SecuredSharedPreferences {
const val KEY_GOOGLE_REFRESH_TOKEN = "GOOGLE_REFRESH_TOKEN"
}
class Services(
/**https://developers.google.com/people/api/rest/v1/people*/
val peopleService: PeopleService.People,
/**https://developers.google.com/people/api/rest/v1/otherContacts*/
val otherContactsService: PeopleService.OtherContacts,
/**https://developers.google.com/people/api/rest/v1/contactGroups*/
val contactGroups: PeopleService.ContactGroups,
)
}
我在 GitHub 上发布了 demo project。
背景
对于我正在开发的应用程序,它使用人员 API 使用凭据(用户登录)。一旦用户提供凭据,我就可以访问各种 Google API,例如 People API。一个例子是获取联系人列表:
https://developers.google.com/people/api/rest/v1/people.connections/list
我注意到 class com.google.api.client.googleapis.auth.oauth2.GoogleCredential
已被弃用:
问题
该应用程序的旧代码基于一些旧的 G+ 代码 (here),可通过 Google 帐户联系联系人。这是其中最重要部分的一个片段,这让我很难从中迁移出来:
object GoogleOuthHelper {
@WorkerThread
fun setUp(context: Context, serverAuthCode: String?): Services {
val httpTransport: HttpTransport = NetHttpTransport()
val jsonFactory = JacksonFactory.getDefaultInstance()
// Redirect URL for web based applications. Can be empty too.
val redirectUrl = "urn:ietf:wg:oauth:2.0:oob"
// Exchange auth code for access token
val tokenResponse = GoogleAuthorizationCodeTokenRequest(
httpTransport, jsonFactory, GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET,
serverAuthCode, redirectUrl)
.execute()
// Then, create a GoogleCredential object using the tokens from GoogleTokenResponse
val credential = GoogleCredential.Builder()
.setClientSecrets(GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET)
.setTransport(httpTransport)
.setJsonFactory(jsonFactory)
.build()
val accessToken = tokenResponse.accessToken
getDefaultSecuredSharedPreferences(context).edit()
.putString(SecuredSharedPreferences.KEY__GOOGLE_ACCESS_TOKEN, accessToken).apply()
credential.setFromTokenResponse(tokenResponse)
val appPackageName = context.packageName
val peopleServiceApi = PeopleService.Builder(httpTransport, jsonFactory, credential)
.setApplicationName(appPackageName)
.build()
val peopleService = peopleServiceApi.people()
val otherContactsService = peopleServiceApi.otherContacts()
val contactGroups = peopleServiceApi.contactGroups()
return Services(peopleService, otherContactsService, contactGroups)
}
class Services(
/**https://developers.google.com/people/api/rest/v1/people*/
val peopleService: PeopleService.People,
/**https://developers.google.com/people/api/rest/v1/otherContacts*/
val otherContactsService: OtherContacts,
/**https://developers.google.com/people/api/rest/v1/contactGroups*/
val contactGroups: ContactGroups)
}
问题甚至从一开始就存在:
class GoogleCredentials
似乎不接受我在 GoogleCredential
class.
要添加更多内容,此函数将“serverAuthCode”作为参数,该参数来自 GoogleSignInAccount
,但要获取它,我需要使用已弃用的 GoogleApiClient
class :
fun prepareGoogleApiClient(someContext: Context): GoogleApiClient {
val context = someContext.applicationContext ?: someContext
val gso = GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestServerAuthCode(GOOGLE_CLIENT_ID)
.requestEmail()
.requestScopes(
Scope(PeopleServiceScopes.CONTACTS_READONLY),
Scope(PeopleServiceScopes.USERINFO_PROFILE),
Scope(PeopleServiceScopes.USER_EMAILS_READ),
Scope(PeopleServiceScopes.CONTACTS),
Scope(PeopleServiceScopes.CONTACTS_OTHER_READONLY)
)
.build()
return GoogleApiClient.Builder(context)
.addApi(Auth.GOOGLE_SIGN_IN_API, gso)
.build()
}
这就是我用它做的:
val connectionResult = googleApiClient!!.blockingConnect()
if (!connectionResult.isSuccess)
return
val operation = Auth.GoogleSignInApi.silentSignIn(googleApiClient)
val googleSignInResult: GoogleSignInResult = operation.await()
val googleSignInAccount = googleSignInResult.signInAccount
//use googleSignInAccount.serverAuthCode in setUp() function above
Gradle 文件具有以下依赖项:
// https://mvnrepository.com/artifact/com.google.auth/google-auth-library-oauth2-http
implementation 'com.google.auth:google-auth-library-oauth2-http:0.26.0'
// https://github.com/googleapis/google-api-java-client-services/tree/master/clients/google-api-services-people/v1#gradle https://mvnrepository.com/artifact/com.google.apis/google-api-services-people
implementation 'com.google.apis:google-api-services-people:v1-rev20210515-1.31.0'
我试过的
除了查看文档(并且没有看到与我必须处理的内容相似)之外,我尝试在此处写下这个。
很遗憾,我还找不到如何从旧代码中迁移出来的方法。
我试图在那里询问如何迁移 (here and here),但还没有得到答案。
问题
如何从 GoogleCredential
迁移到 GoogleCredentials
,同时仍然使用各种 API,例如人员 API?
换句话说:如何避免在 Android 上使用任何已弃用的 classes(GoogleCredential 和 GoogleApiClient),同时仍然能够使用各种 APIs?
How can I migrate away from
GoogleCredential
toGoogleCredentials
while still using the various APIs such as People API?In other words: How can I avoid using any of those deprecated classes (GoogleCredential and GoogleApiClient), on Android, while still being able to use the various APIs?
虽然您可以使 GoogleCredentials 直接工作,但最好使用从 GoogleCredentials 派生的 class,例如UserCredentials 将像 GoogleCredential 一样适应令牌刷新。 GoogleCredentials 更像是基础 class。
以下代码使用了 UserCredentials。这主要是您提供的内容,但为了演示的目的,我更改了一些凭证存储逻辑。此代码除了 startActivityForResult()
.
serverAuthCode
可从 GoogleSignInAccount 获得。查看 Moving Past GoogleApiClient on how to remove the dependency on GoogleApiClient. I have update my public gist of RestApiActivity from the Google signin quickstart,其中显示了如何使用 GoogleOauthHelper 以及 GoogleApi。
GoogleOauthHelper.kt
object GoogleOauthHelper {
@WorkerThread
fun setUp(context: Context, serverAuthCode: String?): Services {
val httpTransport: HttpTransport = NetHttpTransport()
val jsonFactory = GsonFactory.getDefaultInstance()
// Redirect URL for web based applications. Can be empty too.
val redirectUrl = "urn:ietf:wg:oauth:2.0:oob"
// Patch for demo
val GOOGLE_CLIENT_ID = context.getString(R.string.GOOGLE_CLIENT_ID)
val GOOGLE_CLIENT_SECRET = context.getString(R.string.GOOGLE_CLIENT_SECRET)
var accessToken: AccessToken? = null
var refreshToken =
getDefaultSecuredSharedPreferences(context).getString(
SecuredSharedPreferences.KEY_GOOGLE_REFRESH_TOKEN,
null
)
if (refreshToken == null) {
/* Did we lose the refresh token, or is this the first time? Refresh tokens are only
returned the first time after the user grants us permission to use the API. So, if
this is the first time doing this, we should get a refresh token. If it's not the
first time, we will not get a refresh token, so we will proceed with the access
token alone. If the access token expires (in about an hour), we will get an error.
What we should do is to ask the user to reauthorize the app and go through the
OAuth flow again to recover a refresh token.
See https://developers.google.com/identity/protocols/oauth2#expiration regarding
how a refresh token can become invalid.
*/
val tokenResponse = GoogleAuthorizationCodeTokenRequest(
httpTransport, jsonFactory, GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET,
serverAuthCode, redirectUrl
).execute()
refreshToken = tokenResponse.refreshToken
if (refreshToken != null) {
getDefaultSecuredSharedPreferences(context).edit()
.putString(SecuredSharedPreferences.KEY_GOOGLE_REFRESH_TOKEN, refreshToken)
.apply()
} else {
Log.d("Applog", "No refresh token. Going with access token alone.")
val expiresAtMilliseconds =
Clock.SYSTEM.currentTimeMillis() + tokenResponse.expiresInSeconds * 1000
accessToken = AccessToken(tokenResponse.accessToken, Date(expiresAtMilliseconds))
}
}
Log.d("Applog", "Refresh token: $refreshToken")
// UserCredentials extends GoogleCredentials and permits token refreshing.
val googleCredentials = UserCredentials.newBuilder().run {
clientId = GOOGLE_CLIENT_ID
clientSecret = GOOGLE_CLIENT_SECRET
setRefreshToken(refreshToken)
setAccessToken(accessToken)
build()
}
// Save access token on change
googleCredentials.addChangeListener { oAuth2Credentials ->
saveAccessToken(oAuth2Credentials.accessToken)
}
val requestInitializer: HttpRequestInitializer = HttpCredentialsAdapter(googleCredentials)
val appPackageName = context.packageName
val peopleServiceApi = PeopleService.Builder(httpTransport, jsonFactory, requestInitializer)
.setApplicationName(appPackageName)
.build()
return peopleServiceApi.run { Services(people(), otherContacts(), contactGroups()) }
}
private fun saveAccessToken(accessToken: AccessToken) {
// Persist the token securely.
Log.d("Applog", "Access token has changed: ${accessToken.tokenValue}")
}
// Warning insecure!: Patch for demo.
private fun getDefaultSecuredSharedPreferences(context: Context): SharedPreferences {
return PreferenceManager.getDefaultSharedPreferences(context)
}
// Warning insecure!: Patch for demo.
object SecuredSharedPreferences {
const val KEY_GOOGLE_REFRESH_TOKEN = "GOOGLE_REFRESH_TOKEN"
}
class Services(
/**https://developers.google.com/people/api/rest/v1/people*/
val peopleService: PeopleService.People,
/**https://developers.google.com/people/api/rest/v1/otherContacts*/
val otherContactsService: PeopleService.OtherContacts,
/**https://developers.google.com/people/api/rest/v1/contactGroups*/
val contactGroups: PeopleService.ContactGroups,
)
}
我在 GitHub 上发布了 demo project。