Android 上客户端凭据流的访问令牌
Posted
技术标签:
【中文标题】Android 上客户端凭据流的访问令牌【英文标题】:Access Token for Client Credentials Flow on Android 【发布时间】:2019-01-03 14:39:24 【问题描述】:我正在开发一个使用 Spotify Web API 搜索内容的应用程序。这是从他们的搜索端点完成的,记录在 here。我不想访问任何 Spotify 用户特定的数据。事实上,我不想要求我的用户拥有 Spotify 帐户才能使用我的应用程序。我想做的就是使用 Spotify Web API 来搜索曲目、专辑和/或艺术家。
为了做到这一点,我需要一个访问令牌来使用我已经拥有的客户端密码和客户端 ID 来验证我的应用程序。我正在尝试执行 Spotify 文档中here 概述的客户端凭据流程。但是,我在这样做时遇到了一些麻烦。
在我的应用中,将使用 Spotify Web API 的活动不是启动活动。因此,用户可以根据需要随时进入和退出此活动。截至目前,我正在使用 Retrofit 并遵循 Spotify 文档的指南获取访问令牌。这是我的代码的样子:
private void retrieveAccessToken()
// First, we obtain an instance of SearchClient through our ClientGenerator class
mClient = ClientGenerator.createClient(SearchClient.class);
// We then obtain the client ID and client secret encoded in Base64.
String encodedString = encodeClientIDAndSecret();
// Finally, we initiate the HTTP request and hope to get the access token as a response
Call<TokenResponse> tokenResponseCall = mClient.getAccessToken(encodedString, "client_credentials");
tokenResponseCall.enqueue(new Callback<TokenResponse>()
@Override
public void onResponse(Call<TokenResponse> call, Response<TokenResponse> response)
Log.d(TAG, "on Response: response toString(): " + response.toString());
TokenResponse tokenResponse = null;
if (response.isSuccessful())
tokenResponse = response.body();
Log.d(TAG, "Access token value: " + tokenResponse.getAccessToken());
mAccessToken = tokenResponse.getAccessToken();
@Override
public void onFailure(Call<TokenResponse> call, Throwable t)
Log.d(TAG, "onFailure: request toString():" + call.request().toString());
mAccessToken = "";
);
private String encodeClientIDAndSecret()
String basic = "Basic ";
String clientIDAndSecret = CLIENT_ID + ":" + CLIENT_SECRET;
/*
I use the NO_WRAP flag so that the encoded String is contained within a single line.
Otherwise, there will be new line characters in the encoded String and we don't want to
include those.
*/
byte [] encodedValue = Base64.encode(clientIDAndSecret.getBytes(), Base64.NO_WRAP);
String encodedString = new String(encodedValue);
// The final output needs to have both the encoded String as well as 'Basic ' prepended to it
return basic + encodedString;
我在onCreate()
的末尾调用retrieveAccessToken()
:
@Override
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
// Initializing member variables and views within my activity
retrieveAccessToken();
当活动第一次启动时,一切都按预期工作:我获得了访问令牌并可以开始发出搜索请求。但是,如果我按下后退按钮然后重新输入相同的活动,那就是事情中断的地方。下次调用 retrieveAccessToken()
时,我在 Retrofit
回调中得到响应,但 TokenResponse
对象有一个 400 html 错误代码,这对我来说意味着出了问题。如果我随后调用retrieveAccessToken()
,则当我打印Retrofit
Response
对象的toString()
时,这就是我在Logcat 中的内容:
on Response: response toString(): Responseprotocol=h2, code=400, message=, url=https://api.spotify.com/api/token
为什么会这样?是的,我意识到每次创建活动时都会调用retrieveAccessToken()
,这是非常低效的。但是,我不确定我还能如何提出这个请求。我的一个想法是在活动不再活动时以某种方式保存访问令牌,我试图在onSaveInstanceState()
中这样做。但是,重新启动活动时,onCreate()
中的Bundle
为空......
我也考虑过使用AccountManager
,但我觉得走那条路有点矫枉过正。我将此developer doc 称为指导。然而,难倒我的主要部分是我会做以下事情:
AccountManager am = AccountManager.get(this);
Bundle options = new Bundle();
am.getAuthToken(
myAccount_, // Account retrieved using getAccountsByType()
"Manage your tasks", // Auth scope
options, // Authenticator-specific options
this, // Your activity
new OnTokenAcquired(), // Callback called when a token is successfully acquired
new Handler(new OnError())); // Callback called if an error occurs
我不想打电话给getAccountsByType()
,因为我不想访问 Spotify 用户的帐户数据。我只是希望能够通过 Spotify Web API 发出搜索请求。
我承认这是我第一次处理 OAuth 2.0。处理这一切的最佳方法是什么?最适合我的情况?
【问题讨论】:
【参考方案1】:尝试使用字符集 ISO_8859_1,因为默认 UTF_8 在尝试解码时会丢失一些符号
String encodedString = new String(encodedValue, Charsets.ISO_8859_1);
【讨论】:
以上是关于Android 上客户端凭据流的访问令牌的主要内容,如果未能解决你的问题,请参考以下文章
无法通过具有客户端凭据流的 CloudFlare Worker 对 Spotify API 进行 OAuth
无法通过 Postman 在 Django oauth 工具包客户端凭据授予中获取访问令牌