Android应用中使用OkHttpClient获取Spotify API访问令牌时遇到响应异常问题
解决Spotify API无法通过OkHttpClient获取访问令牌的问题
嘿,我看你遇到的问题其实是对Spotify授权流程的理解有点偏差,咱们一步步来拆解解决:
问题根源:你用错了授权流程
你当前代码里用的是隐式授权流程(response_type=token),这个流程是专门给纯前端应用(比如单页网站、移动端浏览器)设计的——它需要用户手动在浏览器里完成登录、授权操作,而OkHttpClient是后台HTTP客户端,没法处理用户登录的交互步骤,所以你拿到的Spotify登录页面HTML是完全正常的:因为系统检测到没有已登录的用户,就跳转到登录页让用户操作了。
解决方案:选对适合你场景的授权流程
方案1:改用授权码流程(推荐,适合原生APP/后端服务)
Spotify官方更推荐用授权码流程,它更安全,还支持刷新令牌(不用每次都让用户重新授权),步骤分三步:
引导用户在浏览器/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);获取授权码(code)
用户授权后,Spotify会重定向到你设置的REDIRECT_URI,并在URL中带上code和state参数。你需要在你的REDIRECT_URI对应的端点(比如APP的Deep Link、后端接口)中捕获这个code。用授权码换取访问令牌
拿到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
相关产品推荐
相关产品推荐

