Android 7.1.1(API 25)中基于Retrofit2的OAuth2认证问题咨询
在Android 7.1.1(API25)用Retrofit2实现OAuth2认证流程
我来分享下匹配你需求的完整实现方案——在指定Android版本下,通过Activity的onCreate发起OAuth授权请求,onResume捕获授权码,再用Retrofit2完成Token交换的全流程:
一、先确认Manifest的意图过滤器配置
这一步是浏览器能跳转回你App的核心,一定要配置正确,还要注意Activity的启动模式:
<activity android:name=".AuthActivity" android:launchMode="singleTask"> <!-- 关键:避免重复创建实例,确保onResume能拿到回调数据 --> <intent-filter> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.BROWSABLE" /> <!-- 替换成你在OAuth服务器注册的回调URL的scheme和host --> <data android:scheme="your-custom-scheme" android:host="oauth-callback" /> </intent-filter> </activity>
二、AuthActivity核心逻辑实现
1. onCreate中发起授权请求
在启动页或者认证Activity的onCreate里,构造OAuth授权URL并唤起浏览器:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_auth); // 构造授权URL,替换成你的实际参数 String authRequestUrl = "https://your-oauth-server.com/authorize" + "?client_id=your-client-id" + "&redirect_uri=your-custom-scheme://oauth-callback" + "&response_type=code" + "&scope=user_info read_data"; // 启动浏览器打开授权页面 Intent authIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(authRequestUrl)); startActivity(authIntent); }
2. onResume中捕获授权码
用户在浏览器完成授权后,会跳回你的AuthActivity,此时在onResume里解析回调URL中的授权码:
@Override protected void onResume() { super.onResume(); Uri callbackData = getIntent().getData(); if (callbackData != null) { // 提取授权码 String authCode = callbackData.getQueryParameter("code"); if (authCode != null) { // 拿到授权码后,立刻调用Retrofit2换取Access Token exchangeAuthCodeForToken(authCode); } else { // 处理用户拒绝授权或其他错误 String errorMsg = callbackData.getQueryParameter("error_description"); Toast.makeText(this, "授权失败:" + errorMsg, Toast.LENGTH_SHORT).show(); } // 清除Intent数据,防止下次onResume重复解析 setIntent(null); } }
三、用Retrofit2实现Token交换
1. 定义Retrofit接口和实体类
先写接口描述OAuth的Token交换接口,以及响应实体:
// OAuth API接口 public interface OAuthService { @FormUrlEncoded @POST("oauth/token") // 替换成你的OAuth服务器Token接口路径 Call<TokenResponse> fetchAccessToken( @Field("grant_type") String grantType, @Field("client_id") String clientId, @Field("client_secret") String clientSecret, @Field("code") String authCode, @Field("redirect_uri") String redirectUri ); } // Token响应实体类,用Gson解析 public class TokenResponse { @SerializedName("access_token") public String accessToken; @SerializedName("token_type") public String tokenType; @SerializedName("expires_in") public long expiresIn; @SerializedName("refresh_token") public String refreshToken; }
2. 实现Token交换方法
在AuthActivity里实现exchangeAuthCodeForToken方法,完成网络请求:
private void exchangeAuthCodeForToken(String authCode) { // 初始化Retrofit实例 Retrofit retrofit = new Retrofit.Builder() .baseUrl("https://your-oauth-server.com/") // 替换成服务器根路径 .addConverterFactory(GsonConverterFactory.create()) .build(); OAuthService oAuthService = retrofit.create(OAuthService.class); Call<TokenResponse> tokenCall = oAuthService.fetchAccessToken( "authorization_code", "your-client-id", "your-client-secret", // 注意:敏感信息不要硬编码,建议通过安全方式存储 authCode, "your-custom-scheme://oauth-callback" ); tokenCall.enqueue(new Callback<TokenResponse>() { @Override public void onResponse(Call<TokenResponse> call, Response<TokenResponse> response) { if (response.isSuccessful() && response.body() != null) { TokenResponse token = response.body(); // 把Token保存到SharedPreferences或者其他安全存储 saveTokenToStorage(token); // 授权完成,跳转到主页面并关闭当前页 startActivity(new Intent(AuthActivity.this, MainActivity.class)); finish(); } else { Toast.makeText(AuthActivity.this, "Token换取失败", Toast.LENGTH_SHORT).show(); } } @Override public void onFailure(Call<TokenResponse> call, Throwable t) { Toast.makeText(AuthActivity.this, "网络错误:" + t.getMessage(), Toast.LENGTH_SHORT).show(); } }); }
四、几个关键注意点
- 回调URL一致性:确保Manifest里配置的scheme/host,和OAuth服务器注册的回调URL完全一致,包括大小写、路径(如果有的话);
- HTTP请求权限:API25如果访问的是HTTP服务器,需要在Manifest的
<application>标签里添加android:usesCleartextTraffic="true",或者配置network security config; - 启动模式:必须设置
singleTask,否则浏览器跳转回来会创建新的Activity实例,导致onResume拿不到正确的Intent数据; - 敏感信息防护:客户端密钥不要直接写在代码里,建议通过后端代理或者NDK加密存储,避免反编译泄露;
- 错误处理:不要忽略
error参数,用户拒绝授权、授权超时等情况都会返回错误信息,要友好提示用户。
内容的提问来源于stack exchange,提问作者F. Aydemir
相关产品推荐
相关产品推荐

