Android设备上客户端凭据流的访问令牌

时间:2018-07-26 14:40:37

标签: java android oauth-2.0 spotify

我正在开发一个使用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;
}

我在retrieveAccessToken()结尾打给onCreate()

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    // Initializing member variables and views within my activity
    retrieveAccessToken();
}

首次启动活动时,一切都会按预期进行:我获得了访问令牌,可以开始发出搜索请求了。但是,如果我按“后退”按钮,然后重新输入相同的活动,那么事情就会中断。下次调用retrieveAccessToken()时,我在Retrofit回调中收到响应,但是TokenResponse对象有一个400 HTML错误代码,对我来说意味着出了点问题。如果我随后对toString()进行调用,那么在打印Retrofit Response对象的retrieveAccessToken()时,这就是Logcat的功能:

on Response: response toString(): Response{protocol=h2, code=400, message=, url=https://api.spotify.com/api/token}

为什么会这样?是的,我意识到每次创建活动都会调用retrieveAccessToken(),这效率极低。但是,我不确定该如何提出其他要求。我曾经有一个想法是当活动不再处于活动状态时以某种方式保存访问令牌,这是我在onSaveInstanceState()中尝试做的。但是,重新启动活动后,Bundle中的onCreate()为空。...

我也考虑过使用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 个答案:

答案 0 :(得分:0)

请尝试使用字符集ISO_8859_1,因为默认UTF_8在尝试解码时会丢失一些符号

String encodedString = new String(encodedValue, Charsets.ISO_8859_1);