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

Android Studio中如何存储Access Token与Refresh Token?基于Volley的REST API实现场景问询

Great question! Let's break this down based on Android best practices and your existing code setup:

是否应该将令牌存储在数据库中?

It depends on your app's user model:

  • Single-user app (most common for course projects): You don't need a database. Instead, use encrypted shared preferences (via Jetpack Security's EncryptedSharedPreferences). It's lightweight, easy to implement, and designed for storing small, sensitive pieces of data like tokens.
  • Multi-user app (supports switching accounts): A database (like Room) makes sense here. You can store each user's tokens linked to their account ID, but you still need to encrypt the token fields themselves—never store plaintext tokens anywhere.

Critical note: Never store tokens in plaintext (whether in SharedPreferences or a database). Tokens are sensitive and can be misused if accessed by malicious actors. Encryption is non-negotiable here.

调用其他API时如何访问已存储的令牌?

The best approach is to encapsulate token retrieval and attach it automatically to every authenticated API request. Here's how to integrate this with your existing Volley setup:

Step 1: Add Token Storage Logic

First, add methods to save and retrieve encrypted tokens in your Model class (or create a dedicated TokenManager class for better separation of concerns):

// In your Model class
import androidx.security.crypto.EncryptedSharedPreferences;
import androidx.security.crypto.MasterKey;
import android.content.Context;
import android.util.Log;
import java.util.HashMap;
import java.util.Map;

private SharedPreferences getEncryptedPrefs() {
    try {
        MasterKey masterKey = new MasterKey.Builder(mApplication)
                .setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
                .build();

        return EncryptedSharedPreferences.create(
                mApplication,
                "secure_tokens",
                masterKey,
                EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
                EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
        );
    } catch (Exception e) {
        Log.e("TokenStorage", "Failed to create encrypted prefs", e);
        // Fallback to regular SharedPreferences only for testing (not production!)
        return mApplication.getSharedPreferences("fallback_tokens", Context.MODE_PRIVATE);
    }
}

public void saveAuthTokens(Authentication auth) {
    SharedPreferences.Editor editor = getEncryptedPrefs().edit();
    editor.putString("access_token", auth.getAccessToken());
    editor.putString("refresh_token", auth.getRefreshToken());
    editor.apply();
}

public String getAccessToken() {
    return getEncryptedPrefs().getString("access_token", null);
}

public String getRefreshToken() {
    return getEncryptedPrefs().getString("refresh_token", null);
}

Don't forget to add the Jetpack Security dependency to your build.gradle (Module level):

implementation "androidx.security:security-crypto:1.1.0-alpha06"

Step 2: Save Tokens After Login

Update your onLogin callback in the Activity to save the tokens once login succeeds:

@Override
public void onLogin(Authentication authentication){
    if(authentication.getSuccess().equals("true")) {
        model.saveAuthTokens(authentication); // Save encrypted tokens
        model.setAuth(authentication);
        Toast.makeText(LoginActivity.this, "Login success!", Toast.LENGTH_LONG).show();
        Intent intent = new Intent(LoginActivity.this, HomeActivity.class);
        startActivity(intent);
    } else{
        Toast.makeText(LoginActivity.this, "Invalid Login!", Toast.LENGTH_LONG).show();
    }
}

Step 3: Attach Tokens to API Requests

Modify your WebApi class to create authenticated requests that automatically include the access token in the request headers. Add a helper method to build these requests:

// In your WebApi class
private JsonObjectRequest createAuthenticatedRequest(int method, String url, JSONObject body, Response.Listener<JSONObject> successListener, Response.ErrorListener errorListener) {
    return new JsonObjectRequest(method, url, body, successListener, errorListener) {
        @Override
        public Map<String, String> getHeaders() throws AuthFailureError {
            Map<String, String> headers = new HashMap<>();
            String accessToken = mModel.getAccessToken(); // Assume WebApi has access to your Model instance
            if (accessToken != null) {
                headers.put("Authorization", "Bearer " + accessToken);
            }
            return headers;
        }
    };
}

Now, use this helper method for all authenticated API calls. For example, if you have a request to fetch user profile data:

public void fetchUserProfile(APIListener listener) {
    String url = BASE_URL + "/user/profile";
    Response.Listener<JSONObject> successListener = response -> {
        // Handle profile data (pass to listener)
        listener.onProfileFetched(response);
    };
    Response.ErrorListener errorListener = error -> {
        // Handle errors (see next section for token expiration handling)
        Log.e("ProfileRequest", "Error fetching profile", error);
        Toast.makeText(mApplication, "Failed to load profile", Toast.LENGTH_SHORT).show();
    };
    JsonObjectRequest request = createAuthenticatedRequest(Request.Method.GET, url, null, successListener, errorListener);
    mRequestQueue.add(request);
}
额外:处理令牌过期

Eventually, your access token will expire. When the API returns a 401 Unauthorized response, you should use the refresh token to get a new access token, then retry the original request. Here's a simplified example of how to handle this in your error listener:

Response.ErrorListener errorListener = error -> {
    if (error.networkResponse != null && error.networkResponse.statusCode == 401) {
        // Access token expired, try to refresh
        refreshAccessToken(new TokenRefreshListener() {
            @Override
            public void onRefreshSuccess(String newAccessToken) {
                // Update stored token and retry the original request
                model.saveAuthTokens(new Authentication(newAccessToken, model.getRefreshToken(), "true"));
                mRequestQueue.add(createAuthenticatedRequest(Request.Method.GET, url, null, successListener, errorListener));
            }

            @Override
            public void onRefreshFailed() {
                // Redirect to login screen if refresh fails
                Intent intent = new Intent(mApplication, LoginActivity.class);
                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
                mApplication.startActivity(intent);
            }
        });
    } else {
        Toast.makeText(mApplication, "Request failed", Toast.LENGTH_SHORT).show();
    }
};

You'll need to implement the refreshAccessToken method in your WebApi class to call your OAuth refresh endpoint with the stored refresh token.


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.30 08:22:43