服务器时间误差导致的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分执行
能用注解的,尽量不用xml,看着xml就烦!!!
浙公网安备 33010602011771号