当前位置: 首页 > 知识库问答 >
问题:

在Android系统中,用户从Gmail登录后如何获取访问令牌?

仲孙超
2023-03-14

我跟随谷歌登录Android系统。现在我可以得到idToken,但我之前使用的后端服务器正期待访问令牌,因为我之前使用谷歌登录。现在我不想改变我的服务器端。但是我仍然如何使用谷歌登录并在我的Android应用程序中获得访问令牌,以便我可以验证我的用户到我的后端服务器。

我以前使用Google Play服务7.5.0,现在使用Google Play服务最新版本8.3.0。

共有3个答案

百里光熙
2023-03-14

这是我使用静态编程语言的方法,(这是我在StackOverflow上的第一个答案,如果有什么错误,遗漏,或者我可以做得更好,让我知道)

关于登录活动

private fun configureGoogleSignIn() {
    mGoogleSignInOptions = GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
        .requestIdToken(getString(R.string.default_web_client_id))
        .requestServerAuthCode(getString(R.string.server_client_id_oauth))
        .requestEmail()
        .build()
    mGoogleSignInClient = GoogleSignIn.getClient(this, mGoogleSignInOptions)
}

private fun signInWithGoogle() {
    val signInIntent: Intent = mGoogleSignInClient.signInIntent
    startActivityForResult(signInIntent, RC_SIGN_IN)
}

确保调用On创建上的configureGoogleSignIn()函数

然后得到结果

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    super.onActivityResult(requestCode, resultCode, data)

    callbackManager?.onActivityResult(requestCode, resultCode, data)


    if (requestCode == RC_SIGN_IN) {
        val tag = "onActivityResult RC_SIGN_IN"
        val task: Task<GoogleSignInAccount> = GoogleSignIn.getSignedInAccountFromIntent(data)
        try {
            val account = task.getResult(ApiException::class.java)
            firebaseAuthWithGoogle(account!!)
            getIdTokenFromFirebaseAuth()

            var acct = GoogleSignIn.getLastSignedInAccount(this)
            if (acct != null) {
                var personName = acct.displayName
                firstName = acct.givenName!!
                lastName = acct.familyName!!
                userEmail = acct.email!!
                authCode = acct.serverAuthCode!! //THIS is what you looking for
                googleIdToken2 = acct.idToken!!
                Log.d(tag, authCode)
                Log.d(tag, googleIdToken2)
                var personId = acct.id
                //todo pegar foto do google e por no cadastro do usuario
                var personPhoto = acct.photoUrl
                spinner.visibility = View.GONE
                getGoogleAccessToken()
            }
        } catch (e: ApiException) {
            spinner.visibility = View.GONE
            infoToUserTextView.text = getString(R.string.ops_we_had_a_problem)
        }
    }
}

然后调用Google API(我正在使用改型),使用以下界面:

@FormUrlEncoded
@POST
fun getAccessTokenGoogle(
    @Url url: String,
    @Field("grant_type") grant_type: String,
    @Field("client_id") client_id: String,
    @Field("client_secret") client_secret: String,
    @Field("redirect_uri") redirect_uri: String,
    @Field("code") authCode: String,
    @Field("id_token") id_token: String
):Call<GoogleSignInAccessTokenDataClass>

这是Google SignInAccessTokenDataClass

data class GoogleSignInAccessTokenDataClass(
val access_token: String,
val expires_in: Int,
val id_token: String,
val token_type: String

)

在登录活动中进行调用

private fun getGoogleAccessToken(){
    val call = RetrofitGet().userInfoGson().getAccessTokenGoogle(
        grant_type = "authorization_code", client_id = getString(R.string.server_client_id_oauth),
        client_secret = getString(R.string.server_client_secret_oauth), redirect_uri = "",
        authCode = authCode, id_token =googleIdToken2, url = googleTokenUrl
    )

    call.enqueue(object : Callback<GoogleSignInAccessTokenDataClass>{
        val tag = "getGoogleAccessToken"
        override fun onFailure(call: Call<GoogleSignInAccessTokenDataClass>, t: Throwable) {
            Log.e(tag, t.toString())
        }

        override fun onResponse(
            call: Call<GoogleSignInAccessTokenDataClass>,
            response: Response<GoogleSignInAccessTokenDataClass>
        ) {
            if (response.isSuccessful){
                val responseBody = response.body()
                googleAccessToken = responseBody!!.access_token
                Log.d(tag, googleAccessToken)
            }else{
                try {
                    val responseError = response.errorBody()!!.string()
                    Log.e(tag, responseError)
                }catch (e:Exception){Log.e(tag, e.toString())}
            }
        }
    })
}
楚瑞
2023-03-14

BNK在大多数情况下都能做到这一点。Activity类与BNKs answer相同,只是在onActivityResult()方法中获得GoogleSignInAccount后添加OkHttp部分。

但是OkHttp请求部分仍然出错。最后,在Postman中进行了一些测试(和部分运气)后,我发现我缺少id_token参数。OkHttp请求缺少一个参数,即id_token。使用ID令牌,你从GoogleSignIn帐户得到的东西像这样

GoogleSignInAccount acct = result.getSignInAccount();
String idTokenString = acct.getIdToken();

现在将这个idTokenString与BNK答案的OkHttp部分中的所有参数一起使用,有点像这样

...

RequestBody requestBody = new FormEncodingBuilder()
            .add("grant_type", "authorization_code")
            .add("client_id", "alpha-numeric-string-here.apps.googleusercontent.com")
            .add("client_secret", "{clientSecret}")
            .add("redirect_uri","")
            .add("code", "4/4-alphabetic-string-here")
            .add("id_token", idTokenString) // Added this extra parameter here
            .build();

...

得到的回应和BNK的回答一样

{
  "access_token": "ya29.CjBgA_I58IabCJ...remainingAccessTokenHere",
  "token_type": "Bearer",
  "expires_in": 3577,
  "id_token": "eyJhbGciOiJS...veryLongStringHere"
}

现在将这个访问令牌发送到后端服务器进行身份验证,就像在GoogleAuthUtil和PlusAPI时代一样。

希望这能有所帮助:)特别感谢BNK!

满伟彦
2023-03-14

对于您的需求,您可以使用以下代码:

首先,确保您拥有有效的Web OAuth 2.0客户端ID:

<!-- Server Client ID.  This should be a valid Web OAuth 2.0 Client ID obtained
         from https://console.developers.google.com/ -->
    <string name="server_client_id">...e4p8.apps.googleusercontent.com</string>

然后在活动课内:

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    ...

    // For sample only: make sure there is a valid server client ID.
    validateServerClientID();

    // [START configure_signin]
    // Configure sign-in to request offline access to the user's ID, basic
    // profile, and Google Drive. The first time you request a code you will
    // be able to exchange it for an access token and refresh token, which
    // you should store. In subsequent calls, the code will only result in
    // an access token. By asking for profile access (through
    // DEFAULT_SIGN_IN) you will also get an ID Token as a result of the
    // code exchange.
    String serverClientId = getString(R.string.server_client_id);
    GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
            .requestScopes(new Scope(Scopes.DRIVE_APPFOLDER))
            .requestServerAuthCode(serverClientId)
            .requestEmail()
            .build();
    // [END configure_signin]

    // Build GoogleAPIClient with the Google Sign-In API and the above options.
    mGoogleApiClient = new GoogleApiClient.Builder(this)
            .enableAutoManage(this /* FragmentActivity */, this /* OnConnectionFailedListener */)
            .addApi(Auth.GOOGLE_SIGN_IN_API, gso)
            .build();
}

private void getAuthCode() {
    // Start the retrieval process for a server auth code.  If requested, ask for a refresh
    // token.  Otherwise, only get an access token if a refresh token has been previously
    // retrieved.  Getting a new access token for an existing grant does not require
    // user consent.
    Intent signInIntent = Auth.GoogleSignInApi.getSignInIntent(mGoogleApiClient);
    startActivityForResult(signInIntent, RC_GET_AUTH_CODE);
}

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);

    if (requestCode == RC_GET_AUTH_CODE) {
        GoogleSignInResult result = Auth.GoogleSignInApi.getSignInResultFromIntent(data);
        Log.d(TAG, "onActivityResult:GET_AUTH_CODE:success:" + result.getStatus().isSuccess());

        if (result.isSuccess()) {
            // [START get_auth_code]
            GoogleSignInAccount acct = result.getSignInAccount();
            String authCode = acct.getServerAuthCode();

            // Show signed-in UI.
            mAuthCodeTextView.setText(getString(R.string.auth_code_fmt, authCode));
            updateUI(true);

            // TODO(user): send code to server and exchange for access/refresh/ID tokens.
            // [END get_auth_code]
        } else {
            // Show signed-out UI.
            updateUI(false);
        }
    }
}

您可以在下面的ServerAuthCodeActivity中看到完整的代码。JAVA

如果使用该示例,结果如下所示:

然后,你可以按照下面谷歌文档中提到的步骤(从步骤#3.使用HTTPS POST将认证代码发送到你的应用后端):

Google登录为Android-启用服务器端访问

更新:从评论中,如果你想直接从Android客户端应用程序获取访问令牌,请使用以下示例代码(替换为您的client_id,client_secret和授权代码)

OkHttpClient client = new OkHttpClient();
    RequestBody requestBody = new FormEncodingBuilder()
            .add("grant_type", "authorization_code")
            .add("client_id", "812741506391-h38jh0j4fv0ce1krdkiq0hfvt6n5amrf.apps.googleusercontent.com")
            .add("client_secret", "{clientSecret}")
            .add("redirect_uri","")
            .add("code", "4/4-GMMhmHCXhWEzkobqIHGG_EnNYYsAkukHspeYUk9E8")
            .build();
    final Request request = new Request.Builder()
            .url("https://www.googleapis.com/oauth2/v4/token")
            .post(requestBody)
            .build();
    client.newCall(request).enqueue(new Callback() {
        @Override
        public void onFailure(final Request request, final IOException e) {
            Log.e(LOG_TAG, e.toString());                
        }

        @Override
        public void onResponse(Response response) throws IOException {
            try {
                JSONObject jsonObject = new JSONObject(response.body().string());
                final String message = jsonObject.toString(5);
                Log.i(LOG_TAG, message);                    
            } catch (JSONException e) {
                e.printStackTrace();
            }
        }
    });

请使用compile'com。收拾一下。okhttp:okhttp:2.6.0'(版本3-RC1将有不同的类)

有了成功的响应,您将在logcat中获得以下信息:

I/onResponse: {
              "expires_in": 3600,
              "token_type": "Bearer",
              "refresh_token": "1\/xz1eb0XU3....nxoALEVQ",
              "id_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6IjQxMWY1Ym......yWVsUA",
              "access_token": "ya29.bQKKYah-........_tkt980_qAGIo9yeWEG4"
         }
 类似资料:
  • 在PHP中编码,我尝试初始化Spotify API,不使用重定向uri,只使用client_id和client_secret。我尝试下面的代码:从响应url中提取令牌-Spotify API,但我得到一个NULL结果 我想知道我是否可以在不要求用户登录的情况下访问令牌(参见https://developer.spotify.com/web-api/authorization-guide/#auth

  • 问题内容: 我阅读了LKD 1中的一些段落,但 我无法理解以下内容: 从用户空间访问系统调用 通常,C库提供对系统调用的支持。用户应用程序可以从标准标头中提取函数原型,并与C库链接以使用您的系统调用(或库例程,后者又使用syscall调用)。但是,如果您只是编写了系统调用,则怀疑glibc是否已支持它! 幸运的是,Linux提供了一组宏,用于包装对系统调用的访问。它设置寄存器内容并发出陷阱指令。这

  • 当用户刚刚登录时,我如何在Keycloak服务提供程序接口中获得对当前通过身份验证的用户访问令牌的访问权限? 我假设没有办法在用户存储提供程序中获得当前用户的身份验证上下文,因为在那个时间点上用户还没有身份验证,对吗?password grant是当时获取用户身份验证上下文/令牌的正确方法吗?另一种选择可能是链接SPI,例如使用身份验证SPI并截取那里的令牌。但您似乎无法覆盖现有的身份验证流。最后

  • 我试图允许用户通过谷歌API的帮助通过gmail登录。我可以登录,也可以检索个人信息,但不能检索,刷新和访问令牌,这是在成功登录的用户生成。因为我需要将详细信息发送到服务器,所以我如何检索它。 提前谢了。

  • 下面是security.xml文件,我正在使用它合并spring-security-oauth2。 我必须在spring-security.xml文件中做哪些更改,以便在JSON中发送查询参数?

  • 我们创建了一个java web应用程序,一旦用户访问我的网站,它必须将登录id作为他的windows凭据登录id,但在我的情况下,托管我的网站后,它每次都返回服务器管理用户作为windows登录id。在java web应用程序中有没有方法获取当前的windows用户id。 如果我打开我的网站,它必须显示我的名字,如果有些人打开了那个网站,它必须显示他的名字。 谢谢