服务器时间误差导致的google sign-in后台验证错误(远程调试java程序)

https://developers.google.com/identity/sign-in/web/backend-auth

import com.google.api.client.googleapis.auth.oauth2.GoogleIdToken;
import com.google.api.client.googleapis.auth.oauth2.GoogleIdToken.Payload;
import com.google.api.client.googleapis.auth.oauth2.GoogleIdTokenVerifier;

...

GoogleIdTokenVerifier verifier = new GoogleIdTokenVerifier.Builder(new NetHttpTransport(), JacksonFactory.getDefaultInstance())
    .setAudience(Collections.singletonList(CLIENT_ID))
    // Or, if multiple clients access the backend:
    //.setAudience(Arrays.asList(CLIENT_ID_1, CLIENT_ID_2, CLIENT_ID_3))
    .build();

// (Receive idTokenString by HTTPS POST)

GoogleIdToken idToken = verifier.verify(idTokenString);
if (idToken != null) {
  Payload payload = idToken.getPayload();

  // Print user identifier
  String userId = payload.getSubject();
  System.out.println("User ID: " + userId);

  // Get profile information from payload
  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");

  // Use or store profile information
  // ...

} else {
  System.out.println("Invalid ID token.");
}

我后台完全按照google的文档来,却总是输出Invalid ID token,服务器是在香港,本机调试因为在墙内永远是连接超时。
试了若干方法后,我在网上发现远程调试这个神奇的功能。

remote debug

使用的IDE是idea


(可能需要开启服务器对应端口)


我用的是springboot打包的fatjar,如果是部署到容器中,则把启动选项加到对应容器配置中

这时点这个绿色的小虫子就可以进行调试了(可惜看不到控制台输出内容)

tips


善用这个功能加入需要观察的对象 方框内的都是为了找出问题加进去的

查找问题

逐步配合debug查看源码

  public GoogleIdToken verify(String idTokenString) throws GeneralSecurityException, IOException {
    GoogleIdToken idToken = GoogleIdToken.parse(getJsonFactory(), idTokenString);
    return verify(idToken) ? idToken : null;
  }
  public boolean verify(GoogleIdToken googleIdToken) throws GeneralSecurityException, IOException {
    // check the payload
    if (!super.verify(googleIdToken)) {//super.verify(googleIdToken) false
      return false;
    }
    // verify signature, try all public keys in turn.
    for (PublicKey publicKey : publicKeys.getPublicKeys()) {
      if (googleIdToken.verifySignature(publicKey)) {
        return true;
      }
    }
    return false;
  }
  public boolean verify(IdToken idToken) {
    return (issuers == null || idToken.verifyIssuer(issuers))//idToken.verifyIssuer(issuers) true
        && (audience == null || idToken.verifyAudience(audience))//idToken.verifyAudience(audience) true
        && idToken.verifyTime(clock.currentTimeMillis(), acceptableTimeSkewSeconds);//idToken.verifyTime(clock.currentTimeMillis(), acceptableTimeSkewSeconds) false
  }
  public final boolean verifyTime(long currentTimeMillis, long acceptableTimeSkewSeconds) {
    return verifyExpirationTime(currentTimeMillis, acceptableTimeSkewSeconds)//verifyExpirationTime(currentTimeMillis, acceptableTimeSkewSeconds) true
        && verifyIssuedAtTime(currentTimeMillis, acceptableTimeSkewSeconds);//verifyIssuedAtTime(currentTimeMillis, acceptableTimeSkewSeconds) false
  }
  public final boolean verifyIssuedAtTime(long currentTimeMillis, long acceptableTimeSkewSeconds) {
    return currentTimeMillis
        >= (getPayload().getIssuedAtTimeSeconds() - acceptableTimeSkewSeconds) * 1000;
  }

最后currentTimeMillis是下述类中获得的,getPayload().getIssuedAtTimeSeconds()是从解析出的json中获得iat的值,acceptableTimeSkewSeconds=300,于是这里是google服务器返回时间和我们服务器时间在一定范围内(300秒)验证。戏剧性的是我的服务器还正好差了5分钟多一点,于是几乎次次报错,偶尔有一次正常(因为从google获取的时间传到服务器也是需要时间的)。
折磨了我好几天的问题结果只是服务器时间,这里又想吐槽下google的api了,GoogleIdTokenVerifier verifier = new GoogleIdTokenVerifier.Builder(new NetHttpTransport(), JacksonFactory.getDefaultInstance())这行都是我从别的页面找到的结果,它给的一个不用google的库手工验证的方法里也只说了要验证aud,完全没提验证iat这项时间,我也完全没往这边想。
gapi也不是任何时候都能直接调用的,google搜索gapi is not defined一排一排的,完全不提自家js的加载流程,你让咱们猜还是让咱们看源码啊,是不是好让咱创造超越google的互联网公司,出任CEO迎娶白富美走上人生巅峰😠谢谢你咯,我看着你的api调试,还没变强头发都快被我抓秃了。跑题了跑题了😅

package com.google.api.client.util;

/**
 * Clock which can be used to get the amount of elapsed milliseconds in system time.
 *
 * <p>
 * The default system implementation can be accessed at {@link #SYSTEM}. Alternative implementations
 * may be used for testing.
 * </p>
 *
 * @since 1.9
 * @author mlinder@google.com (Matthias Linder)
 */
public interface Clock {
  /**
   * Returns the current time in milliseconds since midnight, January 1, 1970 UTC, to match the
   * behavior of {@link System#currentTimeMillis()}.
   */
  long currentTimeMillis();

  /**
   * Provides the default System implementation of a Clock by using
   * {@link System#currentTimeMillis()}.
   */
  Clock SYSTEM = new Clock() {
    public long currentTimeMillis() {
      return System.currentTimeMillis();
    }
  };
}

解决方案 linux ntpdate命令

程序没有严格的时间要求 故直接使用ntpdate(简单) 若是有timer一类对时间有严格要求的 请网上参阅ntpd(推荐)
yum install ntpdate
ntpdate cn.pool.ntp.org
加入定时任务
crontab -e
10 * * * * /usr/sbin/ntpdate cn.pool.ntp.org每小时10分执行

posted on 2017-04-20 15:51  幽魂步  阅读(2225)  评论(0)    收藏  举报

导航