在服务器上为 GCP Speech to Text 生成 AccessToken 以供 Android/iOS 使用
Generate AccessToken for GCP Speech to Text on server for use in Android/iOS
正在开展一个项目,在 android 和 iOS 环境中集成 Google Cloud 的语音转文本 api。 运行 通过提供的示例代码 (https://cloud.google.com/speech-to-text/docs/samples) 并能够将其获取到 运行。使用它们作为模板将语音添加到我的应用程序中,但是示例中存在严重的危险,特别是在生成 AccessToken 时(下面的 Android 片段):
// ***** WARNING *****
// In this sample, we load the credential from a JSON file stored in a raw resource
// folder of this client app. You should never do this in your app. Instead, store
// the file in your server and obtain an access token from there.
// *******************
final InputStream stream = getResources().openRawResource(R.raw.credential);
try {
final GoogleCredentials credentials = GoogleCredentials.fromStream(stream)
.createScoped(SCOPE);
final AccessToken token = credentials.refreshAccessToken();
这在本地开发和测试很好,但如评论所示,将凭据文件保存到生产应用程序构建中是不安全的。所以我需要做的是用来自服务器端点的请求替换这段代码。此外,我需要编写将接受请求并传回令牌的端点。虽然我发现了一些与生成令牌的 Firebase Admin 库相关的非常有趣的教程,但我找不到任何与为 GCP apis 执行类似操作相关的内容。
任何可以为我指明正确方向的suggestions/documentation/examples都将不胜感激!
注意:服务器端点将是一个 Node.js 环境。
这里有几个有用的链接:
很抱歉耽搁了,我能够让所有的东西一起工作,现在我只是回到 post 一个极其简化的操作方法。首先,我在服务器端点项目 https://www.npmjs.com/package/google-auth-library
上安装了以下库
为简单起见,本例中的服务器端点缺少任何 authentication/authorization 等。我会把那部分留给你。我们还将假装可以从 https://www.example.com/token
访问此端点
预期是,调用 https://www.example.com/token 将产生一个响应,其中包含字符串令牌、过期数字以及有关令牌生成方式的一些额外信息:
即:
{"token":"sometoken", "expires":1234567, "info": {... additional stuff}}
同样对于这个例子,我使用了一个 ServiceAccountKey 文件,该文件将存储在服务器上,
建议的路线是设置一个服务器环境变量并使用 https://cloud.google.com/docs/authentication/production#finding_credentials_automatically 但这只是为了示例,并且对于快速测试来说足够简单。这些文件看起来像下面这样:(荣誉系统不要窃取我的私钥)
ServiceAccountKey.json
{
"type": "service_account",
"project_id": "project-id",
"private_key_id": "378329234klnfgdjknfdgh9fgd98fgduiph",
"private_key": "-----BEGIN PRIVATE KEY-----\nThisIsTotallyARealPrivateKeyPleaseDontStealIt=\n-----END PRIVATE KEY-----\n",
"client_email": "project-id@appspot.gserviceaccount.com",
"client_id": "12345678901234567890",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://oauth2.googleapis.com/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/project-id%40appspot.gserviceaccount.com"
}
所以这是一个简单的端点,它吐出一个 AccessToken 和一个指示令牌何时过期的数字(因此您可以稍后调用一个新的)。
endpoint.js
const express = require("express");
const auth = require("google-auth-library");
const serviceAccount = require("./ServiceAccountKey.json");
const googleauthoptions = {
scopes: ['https://www.googleapis.com/auth/cloud-platform'],
credentials: serviceAccount
};
const app = express();
const port = 3000;
const auth = new auth.GoogleAuth(googleauthoptions);
auth.getClient().then(client => {
app.get('/token', (req, res) => {
client
.getAccessToken()
.then((clientresponse) => {
if (clientresponse.token) {
return clientresponse.token;
}
return Promise.reject('unable to generate an access token.');
})
.then((token) => {
return client.getTokenInfo(token).then(info => {
const expires = info.expiry_date;
return res.status(200).send({ token, expires, info });
});
})
.catch((reason) => {
console.log('error: ' + reason);
res.status(500).send({ error: reason });
});
});
app.listen(port, () => {
console.log(`Server is listening on https://www.example.com:${port}`);
});
return;
});
差不多完成了,将以android为例。第一个剪辑将是它最初从设备文件中提取的方式:
public static final List<String> SCOPE = Collections.singletonList("https://www.googleapis.com/auth/cloud-platform");
final GoogleCredentials credentials = GoogleCredentials.fromStream(this.mContext.getResources().openRawResource(R.raw.credential)).createScoped(SCOPE);
final AccessToken token = credentials.refreshAccessToken();
final string token = accesstoken.getTokenValue();
final long expires = accesstoken.getExpirationTime().getTime()
final SharedPreferences prefs = getSharedPreferences(PREFS, Context.MODE_PRIVATE);
prefs.edit().putString(PREF_ACCESS_TOKEN_VALUE, value).putLong(PREF_ACCESS_TOKEN_EXPIRATION_TIME, expires).apply();
fetchAccessToken();
现在我们通过互联网(未显示)从端点获得令牌,手头有令牌和过期信息,我们以与在设备上生成的方式相同的方式处理它:
//
// lets pretend endpoint contains the results from our internet request against www.example.com/token
final string token = endpoint.token;
final long expires = endpoint.expires
final SharedPreferences prefs = getSharedPreferences(PREFS, Context.MODE_PRIVATE);
prefs.edit().putString(PREF_ACCESS_TOKEN_VALUE, value).putLong(PREF_ACCESS_TOKEN_EXPIRATION_TIME, expires).apply();
fetchAccessToken();
无论如何,希望对有类似需求的人有所帮助。
===== re: AlwaysLearning 评论区=====
与基于原始文件凭证的解决方案相比:
https://github.com/GoogleCloudPlatform/android-docs-samples/blob/master/speech/Speech/app/src/main/java/com/google/cloud/android/speech/SpeechService.java
在我的具体情况下,我通过本机反应环境(位于 android 之上并使用java脚本)。
我已经有一种机制可以与我创建的 api 端点进行安全通信。
所以在概念上我调用了 react native
MyApiEndpoint()
这给了我一个令牌/过期即。
token = "some token from the api" // token info returned from the api
expires = 3892389329237 // expiration time returned from the api
然后我将该信息从 react-native 向下传递到 java,并通过此函数使用存储的信息更新 android pref(我将此函数添加到 SpeechService.java文件)
public void setToken(String value, long expires) {
final SharedPreferences prefs = getSharedPreferences(PREFS, Context.MODE_PRIVATE);
prefs.edit().putString(PREF_ACCESS_TOKEN_VALUE, value).putLong(PREF_ACCESS_TOKEN_EXPIRATION_TIME, expires).apply();
fetchAccessToken();
}
此函数将令牌和过期内容添加到众所周知的共享首选项位置并启动 AccessTokenTask()
AccessTokenTask 已修改为仅从首选项中提取
private class AccessTokenTask extends AsyncTask<Void, Void, AccessToken> {
protected AccessToken doInBackground(Void... voids) {
final SharedPreferences prefs = getSharedPreferences(PREFS, Context.MODE_PRIVATE);
String tokenValue = prefs.getString(PREF_ACCESS_TOKEN_VALUE, null);
long expirationTime = prefs.getLong(PREF_ACCESS_TOKEN_EXPIRATION_TIME, -1);
if (tokenValue != null && expirationTime != -1) {
return new AccessToken(tokenValue, new Date(expirationTime));
}
return null;
}
您可能注意到我在这里没有对过期信息做太多处理,我在其他地方检查过期。
正在开展一个项目,在 android 和 iOS 环境中集成 Google Cloud 的语音转文本 api。 运行 通过提供的示例代码 (https://cloud.google.com/speech-to-text/docs/samples) 并能够将其获取到 运行。使用它们作为模板将语音添加到我的应用程序中,但是示例中存在严重的危险,特别是在生成 AccessToken 时(下面的 Android 片段):
// ***** WARNING *****
// In this sample, we load the credential from a JSON file stored in a raw resource
// folder of this client app. You should never do this in your app. Instead, store
// the file in your server and obtain an access token from there.
// *******************
final InputStream stream = getResources().openRawResource(R.raw.credential);
try {
final GoogleCredentials credentials = GoogleCredentials.fromStream(stream)
.createScoped(SCOPE);
final AccessToken token = credentials.refreshAccessToken();
这在本地开发和测试很好,但如评论所示,将凭据文件保存到生产应用程序构建中是不安全的。所以我需要做的是用来自服务器端点的请求替换这段代码。此外,我需要编写将接受请求并传回令牌的端点。虽然我发现了一些与生成令牌的 Firebase Admin 库相关的非常有趣的教程,但我找不到任何与为 GCP apis 执行类似操作相关的内容。
任何可以为我指明正确方向的suggestions/documentation/examples都将不胜感激!
注意:服务器端点将是一个 Node.js 环境。
这里有几个有用的链接:
很抱歉耽搁了,我能够让所有的东西一起工作,现在我只是回到 post 一个极其简化的操作方法。首先,我在服务器端点项目 https://www.npmjs.com/package/google-auth-library
上安装了以下库为简单起见,本例中的服务器端点缺少任何 authentication/authorization 等。我会把那部分留给你。我们还将假装可以从 https://www.example.com/token
访问此端点预期是,调用 https://www.example.com/token 将产生一个响应,其中包含字符串令牌、过期数字以及有关令牌生成方式的一些额外信息:
即:
{"token":"sometoken", "expires":1234567, "info": {... additional stuff}}
同样对于这个例子,我使用了一个 ServiceAccountKey 文件,该文件将存储在服务器上, 建议的路线是设置一个服务器环境变量并使用 https://cloud.google.com/docs/authentication/production#finding_credentials_automatically 但这只是为了示例,并且对于快速测试来说足够简单。这些文件看起来像下面这样:(荣誉系统不要窃取我的私钥)
ServiceAccountKey.json
{
"type": "service_account",
"project_id": "project-id",
"private_key_id": "378329234klnfgdjknfdgh9fgd98fgduiph",
"private_key": "-----BEGIN PRIVATE KEY-----\nThisIsTotallyARealPrivateKeyPleaseDontStealIt=\n-----END PRIVATE KEY-----\n",
"client_email": "project-id@appspot.gserviceaccount.com",
"client_id": "12345678901234567890",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://oauth2.googleapis.com/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/project-id%40appspot.gserviceaccount.com"
}
所以这是一个简单的端点,它吐出一个 AccessToken 和一个指示令牌何时过期的数字(因此您可以稍后调用一个新的)。
endpoint.js
const express = require("express");
const auth = require("google-auth-library");
const serviceAccount = require("./ServiceAccountKey.json");
const googleauthoptions = {
scopes: ['https://www.googleapis.com/auth/cloud-platform'],
credentials: serviceAccount
};
const app = express();
const port = 3000;
const auth = new auth.GoogleAuth(googleauthoptions);
auth.getClient().then(client => {
app.get('/token', (req, res) => {
client
.getAccessToken()
.then((clientresponse) => {
if (clientresponse.token) {
return clientresponse.token;
}
return Promise.reject('unable to generate an access token.');
})
.then((token) => {
return client.getTokenInfo(token).then(info => {
const expires = info.expiry_date;
return res.status(200).send({ token, expires, info });
});
})
.catch((reason) => {
console.log('error: ' + reason);
res.status(500).send({ error: reason });
});
});
app.listen(port, () => {
console.log(`Server is listening on https://www.example.com:${port}`);
});
return;
});
差不多完成了,将以android为例。第一个剪辑将是它最初从设备文件中提取的方式:
public static final List<String> SCOPE = Collections.singletonList("https://www.googleapis.com/auth/cloud-platform");
final GoogleCredentials credentials = GoogleCredentials.fromStream(this.mContext.getResources().openRawResource(R.raw.credential)).createScoped(SCOPE);
final AccessToken token = credentials.refreshAccessToken();
final string token = accesstoken.getTokenValue();
final long expires = accesstoken.getExpirationTime().getTime()
final SharedPreferences prefs = getSharedPreferences(PREFS, Context.MODE_PRIVATE);
prefs.edit().putString(PREF_ACCESS_TOKEN_VALUE, value).putLong(PREF_ACCESS_TOKEN_EXPIRATION_TIME, expires).apply();
fetchAccessToken();
现在我们通过互联网(未显示)从端点获得令牌,手头有令牌和过期信息,我们以与在设备上生成的方式相同的方式处理它:
//
// lets pretend endpoint contains the results from our internet request against www.example.com/token
final string token = endpoint.token;
final long expires = endpoint.expires
final SharedPreferences prefs = getSharedPreferences(PREFS, Context.MODE_PRIVATE);
prefs.edit().putString(PREF_ACCESS_TOKEN_VALUE, value).putLong(PREF_ACCESS_TOKEN_EXPIRATION_TIME, expires).apply();
fetchAccessToken();
无论如何,希望对有类似需求的人有所帮助。
===== re: AlwaysLearning 评论区=====
与基于原始文件凭证的解决方案相比: https://github.com/GoogleCloudPlatform/android-docs-samples/blob/master/speech/Speech/app/src/main/java/com/google/cloud/android/speech/SpeechService.java
在我的具体情况下,我通过本机反应环境(位于 android 之上并使用java脚本)。
我已经有一种机制可以与我创建的 api 端点进行安全通信。
所以在概念上我调用了 react native
MyApiEndpoint()
这给了我一个令牌/过期即。
token = "some token from the api" // token info returned from the api
expires = 3892389329237 // expiration time returned from the api
然后我将该信息从 react-native 向下传递到 java,并通过此函数使用存储的信息更新 android pref(我将此函数添加到 SpeechService.java文件)
public void setToken(String value, long expires) {
final SharedPreferences prefs = getSharedPreferences(PREFS, Context.MODE_PRIVATE);
prefs.edit().putString(PREF_ACCESS_TOKEN_VALUE, value).putLong(PREF_ACCESS_TOKEN_EXPIRATION_TIME, expires).apply();
fetchAccessToken();
}
此函数将令牌和过期内容添加到众所周知的共享首选项位置并启动 AccessTokenTask()
AccessTokenTask 已修改为仅从首选项中提取
private class AccessTokenTask extends AsyncTask<Void, Void, AccessToken> {
protected AccessToken doInBackground(Void... voids) {
final SharedPreferences prefs = getSharedPreferences(PREFS, Context.MODE_PRIVATE);
String tokenValue = prefs.getString(PREF_ACCESS_TOKEN_VALUE, null);
long expirationTime = prefs.getLong(PREF_ACCESS_TOKEN_EXPIRATION_TIME, -1);
if (tokenValue != null && expirationTime != -1) {
return new AccessToken(tokenValue, new Date(expirationTime));
}
return null;
}
您可能注意到我在这里没有对过期信息做太多处理,我在其他地方检查过期。