You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android Studio中Google Drive授权异常:调用API返回401未授权

Google Drive API调用返回401未授权问题解决

我开发Google Drive客户端时,完成认证与授权后调用Google Drive API获取文件列表,收到401未授权错误。

认证获取Token的代码(原代码)

class LogginViewModel: ViewModel() {
    private val _loginResult = MutableLiveData<Boolean>()
    val loginResult: LiveData<Boolean> get() = _loginResult

    private val _loginData = MutableLiveData<String>()
    val loginData: LiveData<String> get() = _loginData

    fun signInGoogle(context: Context) {
        val credentialManager = CredentialManager.create(context)

        val getGoogleIdOption: GetGoogleIdOption = GetGoogleIdOption.Builder()
            .setFilterByAuthorizedAccounts(false)
            .setServerClientId("77699025246-l0t92qgqq3bc8d699vj3o5cixxxxxxx.apps.googleusercontent.com")
            .setNonce(generateNonce())
            .build()
        

        val request: GetCredentialRequest = GetCredentialRequest.Builder()
            .addCredentialOption(getGoogleIdOption)
            .build()

        viewModelScope.launch(Dispatchers.Main) {
            try {
                val result = credentialManager.getCredential(
                    request = request,
                    context = context,
                )

                val credential = result.credential

                val googleIdTokenCredential = GoogleIdTokenCredential
                    .createFrom(credential.data)

                val googleIdToken = googleIdTokenCredential.idToken

                _loginData.value = googleIdToken
                _loginResult.value = true

            } catch (e: androidx.credentials.exceptions.GetCredentialException) {
                _loginData.value = "1" + e.message
                _loginResult.value = false
            } catch (e: GoogleIdTokenParsingException) {
                _loginData.value = "2" + e.message
                _loginResult.value = false
            }
        }
    }

    private fun generateNonce(): String {
        val rawNonce = UUID.randomUUID().toString()
        val bytes = rawNonce.toByteArray()
        val md = MessageDigest.getInstance("SHA-256")
        val digest = md.digest(bytes)
        return digest.fold("") {str, it -> str + "%02x".format(it) }
    }
}

调用Drive API的代码(原代码)

class MenuViewModel: ViewModel() {
    private val _driveFiles = MutableLiveData<List<com.google.api.services.drive.model.File>>()
    val driveFiles: LiveData<List<com.google.api.services.drive.model.File>> get() = _driveFiles

    fun fetchDriveFiles(googleIdToken: String) {
        viewModelScope.launch(Dispatchers.IO) {
            try {
                val HTTP_TRANSPORT = GoogleNetHttpTransport.newTrustedTransport()
                val JSON_FACTORY = GsonFactory.getDefaultInstance()

                val credential = GoogleCredential().setAccessToken(googleIdToken)

                val driveService = Drive.Builder(HTTP_TRANSPORT, JSON_FACTORY, credential)
                    .setApplicationName("Drive API Kotlin Example")
                    .build()

                val result: FileList = driveService.files().list()
                    .setPageSize(10)
                    .setFields("nextPageToken, files(id, name)")
                    .execute()

                val files = result.files
                if (files != null && files.isNotEmpty()) {
                    _driveFiles.postValue(files)
                } else {
                    _driveFiles.postValue(emptyList())
                }
            } catch (e: Exception) {
                e.printStackTrace()
            }
        }
    }
}

问题根源

你混淆了Google ID Token和OAuth 2.0 Access Token的用途:

  • Google ID Token仅用于验证用户身份(确认用户属于某个谷歌账号),不具备调用Google Drive API的权限。
  • Google Drive API需要的是带有Drive权限范围的OAuth 2.0 Access Token,这个Token才是授权API访问的有效凭证。

修复步骤

1. 修改认证流程,请求Drive API权限并获取Access Token

更新LogginViewModel中的认证逻辑,添加Drive API的权限范围,并提取正确的Access Token:

fun signInGoogle(context: Context) {
    val credentialManager = CredentialManager.create(context)

    val getGoogleIdOption: GetGoogleIdOption = GetGoogleIdOption.Builder()
        .setFilterByAuthorizedAccounts(false)
        .setServerClientId("77699025246-l0t92qgqq3bc8d699vj3o5cixxxxxxx.apps.googleusercontent.com")
        .setNonce(generateNonce())
        // 添加Drive只读权限,按需可更换为drive.file(仅授权用户选择的文件)或drive(全权限)
        .setScopes(listOf("https://www.googleapis.com/auth/drive.readonly"))
        .build()
    

    val request: GetCredentialRequest = GetCredentialRequest.Builder()
        .addCredentialOption(getGoogleIdOption)
        .build()

    viewModelScope.launch(Dispatchers.Main) {
        try {
            val result = credentialManager.getCredential(
                request = request,
                context = context,
            )

            val credential = result.credential

            // 替换为解析OAuth2 Token凭证,提取Access Token
            val googleOAuth2TokenCredential = GoogleOAuth2TokenCredential.createFrom(credential.data)
            val accessToken = googleOAuth2TokenCredential.accessToken

            _loginData.value = accessToken // 现在存储的是可用于API调用的Access Token
            _loginResult.value = true

        } catch (e: androidx.credentials.exceptions.GetCredentialException) {
            _loginData.value = "1" + e.message
            _loginResult.value = false
        } catch (e: GoogleOAuth2TokenParsingException) {
            _loginData.value = "2" + e.message
            _loginResult.value = false
        }
    }
}

2. 检查Google Cloud项目配置

  • 确保已在Google Cloud控制台启用Google Drive API;
  • 在OAuth同意屏幕中添加了对应的Drive权限范围;
  • 确认使用的客户端ID(Server Client ID)已正确关联到你的Android应用(如果是Android客户端,建议使用Android类型的客户端ID)。

3. 调用Drive API的代码调整(可选)

现在传入fetchDriveFiles的是合法的Access Token,原代码可正常运行,建议修改参数名增强可读性:

fun fetchDriveFiles(accessToken: String) {
    viewModelScope.launch(Dispatchers.IO) {
        try {
            val HTTP_TRANSPORT = GoogleNetHttpTransport.newTrustedTransport()
            val JSON_FACTORY = GsonFactory.getDefaultInstance()

            val credential = GoogleCredential().setAccessToken(accessToken)

            val driveService = Drive.Builder(HTTP_TRANSPORT, JSON_FACTORY, credential)
                .setApplicationName("Drive API Kotlin Example")
                .build()

            val result: FileList = driveService.files().list()
                .setPageSize(10)
                .setFields("nextPageToken, files(id, name)")
                .execute()

            val files = result.files
            _driveFiles.postValue(files ?: emptyList())
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }
}

内容的提问来源于stack exchange,提问作者Lekr01d

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 05:44:53