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

Android应用中使用OkHttpClient获取Spotify API访问令牌时遇到响应异常问题

解决Spotify API无法通过OkHttpClient获取访问令牌的问题

嘿,我看你遇到的问题其实是对Spotify授权流程的理解有点偏差,咱们一步步来拆解解决:

问题根源:你用错了授权流程

你当前代码里用的是隐式授权流程(response_type=token),这个流程是专门给纯前端应用(比如单页网站、移动端浏览器)设计的——它需要用户手动在浏览器里完成登录、授权操作,而OkHttpClient是后台HTTP客户端,没法处理用户登录的交互步骤,所以你拿到的Spotify登录页面HTML是完全正常的:因为系统检测到没有已登录的用户,就跳转到登录页让用户操作了。

解决方案:选对适合你场景的授权流程

方案1:改用授权码流程(推荐,适合原生APP/后端服务)

Spotify官方更推荐用授权码流程,它更安全,还支持刷新令牌(不用每次都让用户重新授权),步骤分三步:

  1. 引导用户在浏览器/WebView中打开授权URL
    生成response_type=code的授权链接,让用户登录并授权:

    // 用随机字符串生成state,防止CSRF攻击
    String state = UUID.randomUUID().toString();
    String scope = "user-read-private user-read-email";
    URIBuilder ub = new URIBuilder("https://accounts.spotify.com/authorize")
            .addParameter("client_id", CLIENT_ID)
            .addParameter("response_type", "code") // 关键:这里改成code
            .addParameter("scope", scope)
            .addParameter("redirect_uri", REDIRECT_URI)
            .addParameter("state", state);
    String authUrl = ub.toString();
    
    // 打开浏览器让用户授权,比如Android中用Intent:
    Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(authUrl));
    startActivity(browserIntent);
    
  2. 获取授权码(code)
    用户授权后,Spotify会重定向到你设置的REDIRECT_URI,并在URL中带上code和state参数。你需要在你的REDIRECT_URI对应的端点(比如APP的Deep Link、后端接口)中捕获这个code。

  3. 用授权码换取访问令牌
    拿到code后,用OkHttpClient向Spotify的令牌接口发送POST请求:

    OkHttpClient client = new OkHttpClient();
    
    // 构建表单请求体,注意参数都是form-data格式
    RequestBody formBody = new FormBody.Builder()
            .add("grant_type", "authorization_code")
            .add("code", "你从redirect_uri获取的code参数")
            .add("redirect_uri", REDIRECT_URI)
            .add("client_id", CLIENT_ID)
            .add("client_secret", CLIENT_SECRET) // 这里需要你的客户端密钥
            .build();
    
    Request request = new Request.Builder()
            .url("https://accounts.spotify.com/api/token")
            .post(formBody)
            .build();
    
    client.newCall(request).enqueue(new Callback() {
        @Override
        public void onFailure(Call call, IOException e) {
            e.printStackTrace();
        }
    
        @Override
        public void onResponse(Call call, Response response) throws IOException {
            if (response.isSuccessful()) {
                String tokenJson = response.body().string();
                // 这里会拿到包含access_token、refresh_token、expires_in的JSON响应
                MainActivity.this.runOnUiThread(() -> {
                    textView.setText("授权成功!\n" + tokenJson);
                });
            } else {
                String errorMsg = response.body().string();
                MainActivity.this.runOnUiThread(() -> {
                    textView.setText("授权失败:\n" + errorMsg);
                });
            }
        }
    });
    

方案2:如果必须用隐式授权(仅适合WebView场景)

如果你是在APP中用WebView承载授权流程,那可以通过监听WebView的URL变化来提取令牌:

WebView authWebView = findViewById(R.id.auth_webview);
authWebView.setWebViewClient(new WebViewClient() {
    @Override
    public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
        String currentUrl = request.getUrl().toString();
        // 检查是否跳转到了你的redirect_uri
        if (currentUrl.startsWith(REDIRECT_URI)) {
            // 解析URL中的哈希参数(隐式授权的令牌在#后面)
            if (currentUrl.contains("#")) {
                String hashPart = currentUrl.split("#")[1];
                String[] paramPairs = hashPart.split("&");
                HashMap<String, String> tokenInfo = new HashMap<>();
                for (String pair : paramPairs) {
                    String[] keyValue = pair.split("=");
                    if (keyValue.length == 2) {
                        tokenInfo.put(keyValue[0], keyValue[1]);
                    }
                }
                // 提取所需的参数
                String accessToken = tokenInfo.get("access_token");
                String tokenType = tokenInfo.get("token_type");
                String expiresIn = tokenInfo.get("expires_in");
                
                // 更新UI
                runOnUiThread(() -> {
                    textView.setText("获取令牌成功:\nAccess Token: " + accessToken);
                });
                // 停止WebView加载
                return true;
            }
        }
        return super.shouldOverrideUrlLoading(view, request);
    }
});
// 加载之前生成的隐式授权URL
authWebView.loadUrl(authUrl);

最后提醒

  • 绝对不要用OkHttpClient直接调用隐式授权的URL,因为它没法处理用户登录交互
  • 授权码流程是更安全的选择,尤其是需要长期访问用户数据的场景,它支持用refresh_token刷新access_token,不用反复让用户授权

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.30 23:52:37