java - Google App Script API 访问,在 android 应用程序中使用 OAuth idToken
问题描述
我正在尝试从 android 应用程序更新 google 电子表格。
我发现这种操作的流程应该是:
- 从 android App 发送请求
- 在 Google App Script 上接收请求
- 在脚本中修改电子表格数据
我使用 Oauth 来授权用户,我收到了与他的会话相关的 idToken。但是,当我创建附加此令牌的 HTTP 请求时,我得到响应 401 未经授权
E/Volley: [676] NetworkUtility.shouldRetryException: Unexpected response code 401 for <HERE GOES URL OF MY SCRIPT>
2021-11-13 20:47:01.498 20626-20626/com.example.e2 W/System.err: com.android.volley.AuthFailureError
2021-11-13 20:47:01.498 20626-20626/com.example.e2 W/System.err: at com.android.volley.toolbox.NetworkUtility.shouldRetryException(NetworkUtility.java:189)
2021-11-13 20:47:01.498 20626-20626/com.example.e2 W/System.err: at com.android.volley.toolbox.BasicNetwork.performRequest(BasicNetwork.java:145)
2021-11-13 20:47:01.499 20626-20626/com.example.e2 W/System.err: at com.android.volley.NetworkDispatcher.processRequest(NetworkDispatcher.java:132)
2021-11-13 20:47:01.499 20626-20626/com.example.e2 W/System.err: at com.android.volley.NetworkDispatcher.processRequest(NetworkDispatcher.java:111)
2021-11-13 20:47:01.499 20626-20626/com.example.e2 W/System.err: at com.android.volley.NetworkDispatcher.run(NetworkDispatcher.java:90)
我应该附加这个令牌的正确方法是什么,让谷歌脚本“知道”这个请求已经过身份验证?
我的脚本代码:
var ssa = SpreadsheetApp.openByUrl('<URL OF MY SPREADSHEET>');
var sheet = ssa.getSheetByName('<NAME OF TAB>');
function doPost(e){
var action = e.parameter.action;
if(action=='ADD'){
return addItem(e);
}
return 'Unsuported operation';
}
function addItem(e){
var date = new Date();
var id = "Item"+sheet.getLastRow();
var data = e.parameter.data;
sheet.appendRow([date,id,data]);
return ContentService.createTextOutput("Inserting Successfull").setMimeType(ContentService.MimeType.TEXT);
}
我的安卓应用程序代码:
public class MainActivity extends AppCompatActivity {
private static final int RC_SIGN_IN = 11;
private static String oAuthAccessToken;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
GoogleSignInAccount account = GoogleSignIn.getLastSignedInAccount(this);
if (account != null) {
oAuthAccessToken = account.getIdToken();
}
Button btn1 = (Button) this.findViewById(R.id.btn_login);
btn1.setOnClickListener((v) -> {
loginViaGoogle();
});
Button btn2 = (Button) this.findViewById(R.id.btn_send);
btn2.setOnClickListener((v) -> {
volleySendDataToGSheet();
});
Button btn3 = (Button) this.findViewById(R.id.btn_logout);
btn3.setOnClickListener((v) -> {
logout();
});
}
@NonNull
private GoogleSignInOptions getGoogleSignOptions() {
return new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestIdToken(getString(R.string.server_client_id))
.requestScopes(
new Scope("https://www.googleapis.com/auth/spreadsheets"),
new Scope("https://www.googleapis.com/auth/drive.scripts"),
new Scope("https://www.googleapis.com/auth/script.projects"),
new Scope("https://www.googleapis.com/auth/script.processes"),
new Scope("https://www.googleapis.com/auth/script.projects.readonly"))
.build();
}
private void logout() {
GoogleSignInOptions gso = getGoogleSignOptions();
GoogleSignInClient mGoogleSignInClient = GoogleSignIn.getClient(this, gso);
mGoogleSignInClient.signOut();
}
private void loginViaGoogle() {
GoogleSignInOptions gso = getGoogleSignOptions();
GoogleSignInClient mGoogleSignInClient = GoogleSignIn.getClient(this, gso);
Intent signInIntent = mGoogleSignInClient.getSignInIntent();
startActivityForResult(signInIntent, RC_SIGN_IN);
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == RC_SIGN_IN) {
Task<GoogleSignInAccount> task = GoogleSignIn.getSignedInAccountFromIntent(data);
handleSignInResult(task);
}
}
private void handleSignInResult(Task<GoogleSignInAccount> completedTask) {
try {
GoogleSignInAccount account = completedTask.getResult(ApiException.class);
oAuthAccessToken = account.getIdToken();
Log.w(TAG, "Login success, user: " + account.getDisplayName() + ", token length:" + oAuthAccessToken.length());
} catch (ApiException e) {
Log.w(TAG, "signInResult:failed code=" + e.getStatusCode());
}
}
private void volleySendDataToGSheet() {
String googleScriptAppUrl = "<URL OF MY SCRIPT>";
StringRequest request = new StringRequest(Request.Method.POST, googleScriptAppUrl,
successResponse -> {
Toast.makeText(this, "success: " + successResponse, Toast.LENGTH_LONG).show();
},
volleyError -> {
String message = "error: " + volleyError.getMessage() + " " + volleyError.networkResponse.statusCode;
Toast.makeText(this, message, Toast.LENGTH_LONG).show();
volleyError.printStackTrace();
}
) {
@Override
protected Map<String, String> getParams() {
Map<String, String> params = new HashMap<>();
params.put("action", "ADD");
params.put("data", "Data value");
Log.i(TAG, "sending params: " + params.toString());
return params;
}
@Override
public Map<String, String> getHeaders() throws AuthFailureError {
HashMap<String, String> headers = new HashMap<String, String>();
headers.put("Content-Type", "application/json");
headers.put("idToken", oAuthAccessToken);
headers.put("id_token", oAuthAccessToken);
headers.put("accessToken", oAuthAccessToken);
headers.put("access_token", oAuthAccessToken);
headers.put("authorization", oAuthAccessToken);
headers.put("Authorization", oAuthAccessToken);
Log.i(TAG, "sending headers:" + headers.toString());
return headers;
}
};
int socketTimeout = 50000;
int retries = 1;
request.setRetryPolicy(new DefaultRetryPolicy(socketTimeout, retries, DefaultRetryPolicy.DEFAULT_BACKOFF_MULT));
RequestQueue requestQueue = Volley.newRequestQueue(this);
requestQueue.add(request);
}
}
Google App Script 本身运行良好,当我使用 chrome Postman 扩展对其进行测试时,它会正确地将数据添加到电子表格中。所以我希望它只是修复android请求中的授权标头的问题。
提前致谢!
解决方案
我找到了解决此授权问题的方法。如果我部署我的 Google Apps 脚本,允许任何人执行它,它不会拒绝我的 401 请求。
实际上,对于这个项目,授权用户不需要使用我的 API,所以我接受了它现在的工作方式。
否则可能需要承载授权,正如@TheMaster 提到的
推荐阅读
- r - geom_dotplot 颜色的随机模式
- java - 无法从 LavaPlayer 的 Javax 实现中解析 NotificationListener
- php - 如何在 CSS 中查找和删除正文选择器
- r - 使用作闪亮仪表板标题的图像完全适合标题框
- javascript - 当cookie即将过期时发出警报jquery
- android - 无法启动接收器:不允许启动服务意图;应用程序在后台
- android - 如何设置暴露的下拉弹出窗口的背景颜色?
- php - php exec() 权限 - soffice 命令在终端中有效,但在 php exec() 中无效
- css - 来自托管在不同服务器上的 flaticon 的自定义图标字体不起作用
- django - 从一条线上执行两名芹菜工人