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

OAuth 客户端凭据重新颁发访问令牌与刷新令牌

无法获取客户端凭据访问令牌来授权 Power BI

无法通过 Postman 在 Django oauth 工具包客户端凭据授予中获取访问令牌

使用 Keycloak 作为 Azure AD 的身份代理授予客户端凭据

AAD B2C 中具有客户端凭据授予流的 Azure 应用服务轻松身份验证