简体   繁体   English

Oauth2 使用 Dagger2 刷新 Android

[英]Oauth2 refresh with Dagger2 Android

I'm trying to implement Oauth2 login with Dagger2.我正在尝试使用 Dagger2 实现 Oauth2 登录。 Once the access_token gets expired, I have successfully generated new access_token through the refresh_token, but the Authenticator goes on infinite loop once refresh_token is also expired.一旦 access_token 过期,我已经通过 refresh_token 成功生成了新的 access_token,但是一旦 refresh_token 也过期,Authenticator 就会进入无限循环。

This is my Network module, where I defined, Authenticator and Interceptor in OkHttp Client这是我的网络模块,我在 OkHttp Client 中定义了 Authenticator 和 Interceptor

   @Module
   public class NetworkModule
   {
       @Provides
       @Singleton
       OkHttpClient provideOkHttpClient(TokenAuthenticator tokenAuthenticator, TokenInceptor tokenInceptor, SharedManager sharedManager)
       {
           HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
          logging.setLevel(HttpLoggingInterceptor.Level.BODY);
   
           OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
           // adding socket time for read/write/reconnect
           httpClient.connectTimeout(30, TimeUnit.SECONDS);
           httpClient.writeTimeout(30, TimeUnit.SECONDS);
           httpClient.readTimeout(30, TimeUnit.SECONDS);
           // setting the accept type of the request to application/json
           httpClient.addNetworkInterceptor(new Interceptor()
           {
               @Override
               public Response intercept(Chain chain) throws IOException {
                   Request.Builder requestBuilder = chain.request().newBuilder();
                   requestBuilder.header("Accept", "application/json");
                   return chain.proceed(requestBuilder.build());
              }
           });
           httpClient.addInterceptor(logging).addInterceptor(tokenInceptor);
           httpClient.authenticator(tokenAuthenticator);
           return httpClient.build();
       }
    }

       @Provides
       Retrofit provideRetrofit(OkHttpClient okHttpClient){
           return new Retrofit.Builder()
            .baseUrl(ApiConstants.API_BASE_URL)
            .addConverterFactory(GsonConverterFactory.create())
            .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
            .client(okHttpClient)
            .build();
       }

       @Provides
       @Singleton
       ApiService provideApiService(Retrofit retrofit, TokenService apiServiceHolder)
       {
           ApiService apiService = retrofit.create(ApiService.class);
           apiServiceHolder.setApiService(apiService);
           return apiService;
       }

       @Provides
       @Singleton
       public SharedPreferences providePreferences(Application application)
       {
           return application.getSharedPreferences(Constants.APP_PREFERENCES, Context.MODE_PRIVATE);
       }
   
       @Provides
       @Singleton
       public SharedManager provideSharedManager(SharedPreferences sharedPreferences)
       {
           return new SharedManager(sharedPreferences);
       }

       @Provides
       @Singleton
       public TokenAuthenticator tokenAuthenticator(TokenService tokenService, SharedManager sharedManager)
       {
           return new TokenAuthenticator(tokenService, sharedManager);
       }

       @Provides
       @Singleton
       public TokenInceptor tokenInceptor(SharedManager sharedManager)
       {
           return new TokenInceptor(sharedManager);
       }

       @Provides
       @Singleton
       public TokenService apiServiceHolder()
       {
    return new TokenService();
       }
   }

Here's the Interceptor这是拦截器

             @Singleton
   public class TokenInceptor implements Interceptor
   {
       SharedManager sharedManager;
       @Inject
       public TokenInceptor(SharedManager sharedManager)
       {
           this.sharedManager = sharedManager;
       }
       @Override
       public Response intercept(Chain chain) throws IOException
       {
           Request request = chain.request();

           // we don't need header in login/register so, we remove the header from these api request endpoints
           if(request.url().encodedPath().contains("/token/client") && request.method().equalsIgnoreCase("POST"))
           {
               return chain.proceed(request);
           }

           // then we add the authenticator to other api requests
           HttpUrl url = request.url();
           Request.Builder urlBuilder = request.newBuilder().addHeader(ApiConstants.AUTHORIZATION, sharedManager.getBearer()).url(url);
           Request apiRequest = urlBuilder.build();
           return chain.proceed(apiRequest);
       }
   }

Here's the Authenticator这是身份验证器

        @Singleton
    public class TokenAuthenticator implements Authenticator
    {
        private SharedManager sharedManager;
        private TokenService tokenService;
            
        @Inject
        public TokenAuthenticator(@NonNull TokenService apiServiceHolder, SharedManager sharedManager)
        {
            this.tokenService = apiServiceHolder;
            this.sharedManager = sharedManager;
        }

        @Nullable
        @Override
        public Request authenticate(Route route, Response response) throws IOException
        {
            if(!response.request().header(ApiConstants.AUTHORIZATION).equals(sharedManager.getBearer()))
            {
                return null;
            }

            retrofit2.Response<TokenResponse> tokenResponse = tokenService.getApiService().refreshToken(sharedManager.getRefresh()).execute();

            TokenResponse responseData = tokenResponse.body();

            if(tokenResponse.isSuccessful() && responseData!= null)
            {
                TokenResponse responseRequest = (TokenResponse) tokenResponse.body();
                String new_token = responseRequest.getAccess();
                sharedManager.saveAccessToken(new_token);
                return response.request().newBuilder().header(ApiConstants.AUTHORIZATION,sharedManager.getBearer()).build();
            }
            else
            {
                // As per my assumption, the refresh token might expire here
                Log.e("refresh_token","expired");
            }
            return null;
        }
    }

Here's the TokenService class这是 TokenService class

       public class TokenService
   {
       ApiService apiService = null;

       @Nullable
       public ApiService getApiService() {
           return apiService;
       }

       public void setApiService(ApiService apiService) {
           this.apiService = apiService;
       }
   }

Here's SharedManager class这是 SharedManager class

        public class SharedManager
    {
        private SharedPreferences sharedPreferences;
        @Inject
        public SharedManager(SharedPreferences sharedPreferences)
        {this.sharedPreferences = sharedPreferences;};

        public void saveAccessToken(String token)
        {
            sharedPreferences.edit().putString(ApiConstants.ACCESS_TOKEN, token).commit();
        }
        public void saveRefreshToken(String token)
        {
            sharedPreferences.edit().putString(ApiConstants.REFRESH, token).commit();
        }
        public String getAccessToken()
        {
            return sharedPreferences.getString(ApiConstants.ACCESS_TOKEN, "");
        }
        public String getRefresh()
        {
            return sharedPreferences.getString(ApiConstants.REFRESH, "");
        }
        public String getBearer()
        {
           return "Bearer "+getAccessToken();
        }
        public void clearAll()
        {
            sharedPreferences.edit().clear().commit();
        }
    }

Here's ApiService interface这是 ApiService 接口

       public interface ApiService
   {
       // client login
       @POST("token/client")
       @FormUrlEncoded
       Call<ResponseBody> loginUser(@Field("email") String email,
                             @Field("password") String password);

       // method for refresh token
       @POST("token/refresh")
       @FormUrlEncoded
       Call<TokenResponse> refreshToken(@Field("refresh") String refresh);
       
         // get agent
       @GET("agent")
       Call<ResponseBody> getAgentTour();

   }

Can anyone trace out the faults in the code here?任何人都可以在这里找出代码中的错误吗? The code structure changed while posting in stack.在堆栈中发布时代码结构发生了变化。

A standard refresh token grant message will return an error code of invalid_grant when the refresh token finally expires.当刷新令牌最终到期时,标准的刷新令牌授予消息将返回错误代码invalid_grant

{
  "error": "invalid_grant",
  "error_description": "An optional description message that varies between vendors"
}

At this point you should do two things:此时你应该做两件事:

  • For any in flight API calls, throw an exception with an error code such as 'login_required', that your error handling code can silently ignore对于任何正在进行的 API 调用,抛出一个带有错误代码(例如“login_required”)的异常,您的错误处理代码可以忽略它
  • Then perform a login redirect to start a new user session然后执行登录重定向以启动新用户 session

SAMPLE CODE OF MINE我的示例代码

A something to compare against, I have an AppAuth code sample that you can run and which allows simulation of token expiry events:作为对比,我有一个 AppAuth 代码示例,您可以运行它并允许模拟令牌过期事件:

Of course you would need to translate this behaviour to your own Dagger based coding preferences...当然,您需要将此行为转换为您自己的基于 Dagger 的编码首选项......

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM