火力基地 |使用第三方 JWT 库验证 ID 令牌
firebase | Verify ID tokens using a third-party JWT library
正在尝试使用 jjwt 验证 firebase id 令牌。使用 GoogleCredential class 提取私钥。但我不确定这是否正确。收到错误消息:JWT signature does not match locally computed signature.
我应该在此处使用来自服务帐户 json 的私钥吗?也许我误解了 ...setSigningKey(...) 接受的内容。
@Service
public class FirebaseAuthVerifier implements AuthVerifier {
private static final Logger logger = LoggerFactory.getLogger(FirebaseAuthVerifier.class);
@Autowired
private FirebaseProperties fbProps;
public boolean verify(AuthToken token) throws GeneralSecurityException, IOException {
// get google credential
InputStream stream = new FileInputStream("src/main/resources/service-account.json");
ByteArrayOutputStream streamCopy = new ByteArrayOutputStream();
ByteStreams.copy(stream, streamCopy);
stream.close();
GoogleCredential gc = GoogleCredential.fromStream(
new ByteArrayInputStream(streamCopy.toByteArray()),
new NetHttpTransport(),
GsonFactory.getDefaultInstance());
try {
Jwts.parser().setSigningKey(gc.getServiceAccountPrivateKey()).parse(token.getTokenId());
} catch(Exception e) {
// log
logger.info("Firebase auth token verification error: ");
logger.info(e.getMessage());
// claims may have been tampered with
return false;
}
return true;
}
}
您来对了!创建 JWT 以发送到 Google/Firebase 时,将使用来自服务帐户的密钥。你真的不想把它放在你的 APK 中,因为任何恶意的人都可以窃取它并使用它来像你一样创建 ID 令牌!
当您验证来自 Firebase 的令牌时,您需要检查 Firebase 自己的密钥 - 幸运的是,这些是 public!您可以从 https://www.googleapis.com/robot/v1/metadata/x509/securetoken@system.gserviceaccount.com 获取它们 - 它们每隔几个小时轮换一次。如果您查看该文件,您会看到它是一个 JSON 字典,如下所示:
"8226146523a1b8894ba03ad525667b9475d393f5": "---CERT---",
其中的关键是 ID 令牌 JWT header 中的 kid
字段 - 它对应于令牌签名所用的密钥,这意味着对应的证书可用于验证签名。
查看 validating ID tokens 的(服务器端)文档了解更多信息。
使用自定义 jwt id 令牌验证
@Service
public class FirebaseAuthVerifier implements AuthVerifier {
private static final Logger logger = LoggerFactory.getLogger(FirebaseAuthVerifier.class);
private static final String pubKeyUrl = "https://www.googleapis.com/robot/v1/metadata/x509/securetoken@system.gserviceaccount.com";
/**
*
* @param token
* @return
* @throws GeneralSecurityException
* @throws IOException
*/
public boolean verify(AuthToken token) throws GeneralSecurityException, IOException {
// get public keys
JsonObject publicKeys = getPublicKeysJson();
// verify count
int size = publicKeys.entrySet().size();
int count = 0;
// get json object as map
// loop map of keys finding one that verifies
for (Map.Entry<String, JsonElement> entry: publicKeys.entrySet()) {
// log
logger.info("attempting jwt id token validation with: ");
try {
// trying next key
count++;
// get public key
PublicKey publicKey = getPublicKey(entry);
// validate claim set
Jwts.parser().setSigningKey(publicKey).parse(token.getTokenId());
// success, we can return
return true;
} catch(Exception e) {
// log
logger.info("Firebase id token verification error: ");
logger.info(e.getMessage());
// claims may have been tampered with
// if this is the last key, return false
if (count == size) {
return false;
}
}
}
// no jwt exceptions
return true;
}
/**
*
* @param entry
* @return
* @throws GeneralSecurityException
*/
private PublicKey getPublicKey(Map.Entry<String, JsonElement> entry) throws GeneralSecurityException, IOException {
String publicKeyPem = entry.getValue().getAsString()
.replaceAll("-----BEGIN (.*)-----", "")
.replaceAll("-----END (.*)----", "")
.replaceAll("\r\n", "")
.replaceAll("\n", "")
.trim();
logger.info(publicKeyPem);
// generate x509 cert
InputStream inputStream = new ByteArrayInputStream(entry.getValue().getAsString().getBytes("UTF-8"));
CertificateFactory cf = CertificateFactory.getInstance("X.509");
X509Certificate cert = (X509Certificate)cf.generateCertificate(inputStream);
return cert.getPublicKey();
}
/**
*
* @return
* @throws IOException
*/
private JsonObject getPublicKeysJson() throws IOException {
// get public keys
URI uri = URI.create(pubKeyUrl);
GenericUrl url = new GenericUrl(uri);
HttpTransport http = new NetHttpTransport();
HttpResponse response = http.createRequestFactory().buildGetRequest(url).execute();
// store json from request
String json = response.parseAsString();
// disconnect
response.disconnect();
// parse json to object
JsonObject jsonObject = new JsonParser().parse(json).getAsJsonObject();
return jsonObject;
}
}
正在尝试使用 jjwt 验证 firebase id 令牌。使用 GoogleCredential class 提取私钥。但我不确定这是否正确。收到错误消息:JWT signature does not match locally computed signature.
我应该在此处使用来自服务帐户 json 的私钥吗?也许我误解了 ...setSigningKey(...) 接受的内容。
@Service
public class FirebaseAuthVerifier implements AuthVerifier {
private static final Logger logger = LoggerFactory.getLogger(FirebaseAuthVerifier.class);
@Autowired
private FirebaseProperties fbProps;
public boolean verify(AuthToken token) throws GeneralSecurityException, IOException {
// get google credential
InputStream stream = new FileInputStream("src/main/resources/service-account.json");
ByteArrayOutputStream streamCopy = new ByteArrayOutputStream();
ByteStreams.copy(stream, streamCopy);
stream.close();
GoogleCredential gc = GoogleCredential.fromStream(
new ByteArrayInputStream(streamCopy.toByteArray()),
new NetHttpTransport(),
GsonFactory.getDefaultInstance());
try {
Jwts.parser().setSigningKey(gc.getServiceAccountPrivateKey()).parse(token.getTokenId());
} catch(Exception e) {
// log
logger.info("Firebase auth token verification error: ");
logger.info(e.getMessage());
// claims may have been tampered with
return false;
}
return true;
}
}
您来对了!创建 JWT 以发送到 Google/Firebase 时,将使用来自服务帐户的密钥。你真的不想把它放在你的 APK 中,因为任何恶意的人都可以窃取它并使用它来像你一样创建 ID 令牌!
当您验证来自 Firebase 的令牌时,您需要检查 Firebase 自己的密钥 - 幸运的是,这些是 public!您可以从 https://www.googleapis.com/robot/v1/metadata/x509/securetoken@system.gserviceaccount.com 获取它们 - 它们每隔几个小时轮换一次。如果您查看该文件,您会看到它是一个 JSON 字典,如下所示:
"8226146523a1b8894ba03ad525667b9475d393f5": "---CERT---",
其中的关键是 ID 令牌 JWT header 中的 kid
字段 - 它对应于令牌签名所用的密钥,这意味着对应的证书可用于验证签名。
查看 validating ID tokens 的(服务器端)文档了解更多信息。
使用自定义 jwt id 令牌验证
@Service
public class FirebaseAuthVerifier implements AuthVerifier {
private static final Logger logger = LoggerFactory.getLogger(FirebaseAuthVerifier.class);
private static final String pubKeyUrl = "https://www.googleapis.com/robot/v1/metadata/x509/securetoken@system.gserviceaccount.com";
/**
*
* @param token
* @return
* @throws GeneralSecurityException
* @throws IOException
*/
public boolean verify(AuthToken token) throws GeneralSecurityException, IOException {
// get public keys
JsonObject publicKeys = getPublicKeysJson();
// verify count
int size = publicKeys.entrySet().size();
int count = 0;
// get json object as map
// loop map of keys finding one that verifies
for (Map.Entry<String, JsonElement> entry: publicKeys.entrySet()) {
// log
logger.info("attempting jwt id token validation with: ");
try {
// trying next key
count++;
// get public key
PublicKey publicKey = getPublicKey(entry);
// validate claim set
Jwts.parser().setSigningKey(publicKey).parse(token.getTokenId());
// success, we can return
return true;
} catch(Exception e) {
// log
logger.info("Firebase id token verification error: ");
logger.info(e.getMessage());
// claims may have been tampered with
// if this is the last key, return false
if (count == size) {
return false;
}
}
}
// no jwt exceptions
return true;
}
/**
*
* @param entry
* @return
* @throws GeneralSecurityException
*/
private PublicKey getPublicKey(Map.Entry<String, JsonElement> entry) throws GeneralSecurityException, IOException {
String publicKeyPem = entry.getValue().getAsString()
.replaceAll("-----BEGIN (.*)-----", "")
.replaceAll("-----END (.*)----", "")
.replaceAll("\r\n", "")
.replaceAll("\n", "")
.trim();
logger.info(publicKeyPem);
// generate x509 cert
InputStream inputStream = new ByteArrayInputStream(entry.getValue().getAsString().getBytes("UTF-8"));
CertificateFactory cf = CertificateFactory.getInstance("X.509");
X509Certificate cert = (X509Certificate)cf.generateCertificate(inputStream);
return cert.getPublicKey();
}
/**
*
* @return
* @throws IOException
*/
private JsonObject getPublicKeysJson() throws IOException {
// get public keys
URI uri = URI.create(pubKeyUrl);
GenericUrl url = new GenericUrl(uri);
HttpTransport http = new NetHttpTransport();
HttpResponse response = http.createRequestFactory().buildGetRequest(url).execute();
// store json from request
String json = response.parseAsString();
// disconnect
response.disconnect();
// parse json to object
JsonObject jsonObject = new JsonParser().parse(json).getAsJsonObject();
return jsonObject;
}
}