Google 登录服务器实现 returns 401

Google Sign-In server implementation returns 401

尝试从 Play Services 8.1 中的 Google+ 登录切换到 Play Services 8.3 中新的 Google 登录时,服务器端组件停止工作并开始抛出 401每次 API 调用 Google 服务器。

当 Android 应用使用 Google Play Services 8.1 时:

build.gradle:

compile 'com.google.android.gms:play-services-plus:8.1.0'

Android 客户:

mGoogleApiClient = new GoogleApiClient.Builder(this)
         .addApi(Plus.API)
         .addScope(Plus.SCOPE_PLUS_PROFILE)
         .addScope(new Scope("email"))
         .build();
String scopes = "oauth2:profile email";    
accessToken = GoogleAuthUtil.getToken(
        SignInActivity.this, 
        Plus.AccountApi.getAccountName(mGoogleApiClient), 
        scopes); // then send accessToken to the server

服务器端:

 // get user details from Google+ REST API
 GoogleCredential credential = new GoogleCredential().setAccessToken(accessToken);
 Plus plusService = new Plus.Builder(new NetHttpTransport(), new JacksonFactory(), null)
         .setApplicationName("AppName")
         .setHttpRequestInitializer(credential)
         .build();
 Person person = plusService.people().get("me").execute();

切换到 Google Play Services 8.3 时:

build.gradle:

 compile 'com.google.android.gms:play-services-auth:8.3.0'

Android 应用程序:

GoogleSignInOptions googleSignInOptions = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
        .requestServerAuthCode("GOOGLE_CLIENT_ID", false)
        .requestEmail()
        .build();
 mGoogleApiClient = new GoogleApiClient.Builder(this)
         .addApi(Auth.GOOGLE_SIGN_IN_API, googleSignInOptions)
         .build();

使用相同的服务器代码,get("me") 调用 returns:

com.google.api.client.googleapis.json.GoogleJsonResponseException: 401 Unauthorized
{
  "code" : 401,
  "errors" : [ {
    "domain" : "global",
    "location" : "Authorization",
    "locationType" : "header",
    "message" : "Invalid Credentials",
    "reason" : "authError"
  } ],
  "message" : "Invalid Credentials"
}

另请注意,使用新的 Google 登录 Api,只要在 Android 客户端上请求基本配置文件范围,您就可以获得 ID 令牌在您的服务器上交换令牌代码时。如果您要查找的只是全名、电子邮件地址和照片,那应该绰绰有余了。

您将 GoogleSignInAccount.getServerAuthCode() 的结果传递给您的服务器,然后使用如下代码:

HttpTransport transport = new NetHttpTransport();
JacksonFactory jsonFactory = new JacksonFactory();

try {
  // Exchanges the code for tokens.
  GoogleTokenResponse response = new GoogleAuthorizationCodeTokenRequest(
      transport,
      jsonFactory,
      // Very important: Explicitly specify this new endpoint.
      "https://www.googleapis.com/oauth2/v4/token",
      SERVER_CLIENT_ID,
      SERVER_CLIENT_SECRET,
      serverAuthCodeFromAndroid,
      "" /* redirectUri, must be blank with auth code coming from Android */)
    .execute();

  // Get the id token from the exchange result
  // It will be non-null if basic profile scopes are requested 
  // when requesting the serverAuthCode on (Android) client side.
  GoogleIdToken googleIdToken = response.parseIdToken();

  // Verify the id token
  GoogleIdTokenVerifier verifier =
          new GoogleIdTokenVerifier.Builder(transport, jsonFactory)
              // For some old version of libraries
              .setIssuer("https://accounts.google.com")
              .setAudience(Arrays.asList(SERVER_CLIENT_ID))
              .build();
  boolean verified = googleIdToken.verify(verifier);
  if (!verified) {
    // Spoofing!! This is not a legitimate id token issued by 
    // Google to your web application!
    return;
  }

  // Get the profile info.
  GoogleIdToken.Payload payload = googleIdToken.getPayload();
  String userId = payload.getSubject();
  String email = payload.getEmail();
  boolean emailVerified = Boolean.valueOf(payload.getEmailVerified());
  String name = (String) payload.get("name");
  String pictureUrl = (String) payload.get("picture");
  String locale = (String) payload.get("locale");
  String familyName = (String) payload.get("family_name");
  String givenName = (String) payload.get("given_name");
} catch (GeneralSecurityException | IOException e) {
  // error handling
}

不需要 Google+ API!