2015-11-07 45 views
1

有两种方法可以使改造调用同步(使用方法,返回值)和异步(使用回调)。使用OAuth2改进异步调用

第二个异步程序非常适用于开箱即用。但是在涉及到OAuth2身份验证访问时存在一个问题。

你可以推荐我一个很好的RestAdapter,与异步翻新调用兼容。

我试图按如下方式使用拦截器,但它会在主线程上进行网络调用,这对我来说不够(Android)。我试图使用下面的代码(不是我的)。

public class SecuredRestBuilder extends RestAdapter.Builder { 

    private class OAuthHandler implements RequestInterceptor { 

     private boolean loggedIn; 
     private Client client; 
     private String tokenIssuingEndpoint; 
     private String username; 
     private String password; 
     private String clientId; 
     private String clientSecret; 
     private String accessToken; 

     public OAuthHandler(Client client, String tokenIssuingEndpoint, String username, 
       String password, String clientId, String clientSecret) { 
      super(); 
      this.client = client; 
      this.tokenIssuingEndpoint = tokenIssuingEndpoint; 
      this.username = username; 
      this.password = password; 
      this.clientId = clientId; 
      this.clientSecret = clientSecret; 
     } 

     /** 
     * Every time a method on the client interface is invoked, this method is 
     * going to get called. The method checks if the client has previously obtained 
     * an OAuth 2.0 bearer token. If not, the method obtains the bearer token by 
     * sending a password grant request to the server. 
     * 
     * Once this method has obtained a bearer token, all future invocations will 
     * automatically insert the bearer token as the "Authorization" header in 
     * outgoing HTTP requests. 
     * 
     */ 
     @Override 
     public void intercept(RequestFacade request) { 
      // If we're not logged in, login and store the authentication token. 
      if (!loggedIn) { 
       try { 
        // This code below programmatically builds an OAuth 2.0 password 
        // grant request and sends it to the server. 

        // Encode the username and password into the body of the request. 
        FormUrlEncodedTypedOutput to = new FormUrlEncodedTypedOutput(); 
        to.addField("username", username); 
        to.addField("password", password); 

        // Add the client ID and client secret to the body of the request. 
        to.addField("client_id", clientId); 
        to.addField("client_secret", clientSecret); 

        // Indicate that we're using the OAuth Password Grant Flow 
        // by adding grant_type=password to the body 
        to.addField("grant_type", "password"); 

        // The password grant requires BASIC authentication of the client. 
        // In order to do BASIC authentication, we need to concatenate the 
        // client_id and client_secret values together with a colon and then 
        // Base64 encode them. The final value is added to the request as 
        // the "Authorization" header and the value is set to "Basic " 
        // concatenated with the Base64 client_id:client_secret value described 
        // above. 
        String base64Auth = BaseEncoding.base64().encode(new String(clientId + ":" + clientSecret).getBytes()); 
        // Add the basic authorization header 
        List<Header> headers = new ArrayList<Header>(); 
        headers.add(new Header("Authorization", "Basic " + base64Auth)); 

        // Create the actual password grant request using the data above 
        Request req = new Request("POST", tokenIssuingEndpoint, headers, to); 

        // Request the password grant. 
        Response resp = client.execute(req); 

        // Make sure the server responded with 200 OK 
        if (resp.getStatus() < 200 || resp.getStatus() > 299) { 
         // If not, we probably have bad credentials 
         throw new SecuredRestException("Login failure: " 
           + resp.getStatus() + " - " + resp.getReason()); 
        } else { 
         // Extract the string body from the response 
         String body = IOUtils.toString(resp.getBody().in()); 

         // Extract the access_token (bearer token) from the response so that we 
         // can add it to future requests. 
         accessToken = new Gson().fromJson(body, JsonObject.class).get("access_token").getAsString(); 

         // Add the access_token to this request as the "Authorization" 
         // header. 
         request.addHeader("Authorization", "Bearer " + accessToken);  

         // Let future calls know we've already fetched the access token 
         loggedIn = true; 
        } 
       } catch (Exception e) { 
        throw new SecuredRestException(e); 
       } 
      } 
      else { 
       // Add the access_token that we previously obtained to this request as 
       // the "Authorization" header. 
       request.addHeader("Authorization", "Bearer " + accessToken); 
      } 
     } 

     private String username; 
     private String password; 
     private String loginUrl; 
     private String clientId; 
     private String clientSecret = ""; 
     private Client client; 

     @Override 
     public RestAdapter build() { 
      if (username == null || password == null) { 
       throw new SecuredRestException(
         "You must specify both a username and password for a " 
           + "SecuredRestBuilder before calling the build() method."); 
      } 

      if (client == null) { 
       client = new OkClient(); 
      } 
      OAuthHandler hdlr = new OAuthHandler(client, loginUrl, username, password, clientId, clientSecret); 
      setRequestInterceptor(hdlr); 

      return super.build(); 
     } 
    // setters and getters here 
    } 
+0

在不同的论坛上,我被告知这个逻辑分成两个部分:获取访问令牌并发出请求。这将有可能在没有网络呼叫的情况下制造拦截器 – Eduard3192993

回答

1

因此,我最终将RestAdapter类拆分为两个独立的类。第一个获得令牌。另一个是将令牌作为输入的RestAdapter类。

类用于获取令牌:

public class GetTokenRequest { 

    public static final String TAG = GetTokenRequest.class.getCanonicalName(); 

    public static final String CLIENT_ID = AccessPoint.CLIENT_ID; 
    public static final String CLIENT_SECRET = AccessPoint.CLIENT_SECRET; 
    public static final String ENDPOINT = AccessPoint.ENDPOINT; 
    public static final String TOKEN_PATH = AccessPoint.TOKEN_PATH; 

    public interface Listener { 
     void onGetTokenSucess(String token); 
     void onGetTokenUnauthorized(); 
     void onGetTokenFailure(); 
    } 

    public static void getAccessToken(Client client, String username, String password, 
             final Listener callback) { 
     try { 
      // This code below programmatically builds an OAuth 2.0 password 
      // grant request and sends it to the server. 

      // Encode the username and password into the body of the request. 
      FormUrlEncodedTypedOutput to = new FormUrlEncodedTypedOutput(); 
      to.addField("username", username); 
      to.addField("password", password); 

      // Add the client ID and client secret to the body of the request. 
      to.addField("client_id", CLIENT_ID); 
      to.addField("client_secret", CLIENT_SECRET); 

      // Indicate that we're using the OAuth Password Grant Flow 
      // by adding grant_type=password to the body 
      to.addField("grant_type", "password"); 

      // The password grant requires BASIC authentication of the client. 
      // In order to do BASIC authentication, we need to concatenate the 
      // client_id and client_secret values together with a colon and then 
      // Base64 encode them. The final value is added to the request as 
      // the "Authorization" header and the value is set to "Basic " 
      // concatenated with the Base64 client_id:client_secret value described 
      // above. 
      String base64Auth = BaseEncoding.base64() 
        .encode(new String(CLIENT_ID + ":" + CLIENT_SECRET).getBytes()); 
      // Add the basic authorization header 
      List<Header> headers = new ArrayList<Header>(); 
      headers.add(new Header("Authorization", "Basic " + base64Auth)); 

      // Create the actual password grant request using the data above 
      Request req = new Request("POST", ENDPOINT + TOKEN_PATH, headers, to); 

      // Request the password grant. 
      Response resp = client.execute(req); 

      if (resp == null) { 
       Log.e(TAG, "resp is null"); 
       callback.onGetTokenFailure(); 
       return; 
      } 
      int status = resp.getStatus(); 

      // Make sure the server responded with 200 OK 
      if (status >= 200 && status < 300) { 
       Log.e(TAG, "getToken response code is okay"); 
       // Extract the string body from the response 
       final String body = IOUtils.toString(resp.getBody().in()); 

       // Extract the access_token (bearer token) from the response so that we 
       // can add it to future requests. 
       if (callback instanceof LoginActivity) 
        ((LoginActivity) callback).runOnUiThread(new Runnable() { 
         @Override 
         public void run() { 
          callback.onGetTokenSucess(new Gson().fromJson(body, JsonObject.class) 
            .get("access_token").getAsString()); 
         } 
        }); 
      } else if (status == HttpStatus.SC_UNAUTHORIZED 
        || status == HttpStatus.SC_BAD_REQUEST) { 
       Log.e(TAG, "getToken response code is 401"); 
       // Incorrect credentials 
       if (callback instanceof LoginActivity) 
        ((LoginActivity) callback).runOnUiThread(new Runnable() { 
         @Override 
         public void run() { 
          callback.onGetTokenUnauthorized(); 
         } 
        }); 
      } else { 
       // Other error 
       Log.e(TAG, "getToken response code - other"); 
       if (callback instanceof LoginActivity) 
        ((LoginActivity) callback).runOnUiThread(new Runnable() { 
         @Override 
         public void run() { 
          ((LoginActivity) callback).onGetTokenFailure(); 
         } 
        }); 
      } 
     } catch (Exception e) { 
      Log.e(TAG, "Exception caught"); 
      Log.e(TAG, e.toString()); 
      if (callback instanceof LoginActivity) 
       ((LoginActivity) callback).runOnUiThread(new Runnable() { 
        @Override 
        public void run() { 
         callback.onGetTokenFailure(); 
        } 
       }); 
     } 
    } 
} 

RestAdapter类:

public class SecuredRestAdapter extends RestAdapter.Builder { 

    private class OAuthHandler implements RequestInterceptor { 

     private boolean loggedIn; 
     private Client client; 
     private String tokenIssuingEndpoint; 
     private String username; 
     private String password; 
     private String clientId; 
     private String clientSecret; 
     private String accessToken; 

     public OAuthHandler(Client client, String accessToken) { 
      super(); 
      this.client = client; 
      this.accessToken = accessToken; 
     } 

     @Override 
     public void intercept(RequestFacade request) { 
      // Add the access_token that we previously obtained to this request as 
      // the "Authorization" header. 
      request.addHeader("Authorization", "Bearer " + accessToken); 
     } 

    } 

    private String loginUrl; 
    private Client client; 
    private String token; 

    public SecuredRestAdapter setLoginEndpoint(String endpoint){ 
     loginUrl = endpoint; 
     return this; 
    } 

    @Override 
    public SecuredRestAdapter setEndpoint(String endpoint) { 
     return (SecuredRestAdapter) super.setEndpoint(endpoint); 
    } 

    @Override 
    public SecuredRestAdapter setEndpoint(Endpoint endpoint) { 
     return (SecuredRestAdapter) super.setEndpoint(endpoint); 
    } 

    @Override 
    public SecuredRestAdapter setClient(Client client) { 
     this.client = client; 
     return (SecuredRestAdapter) super.setClient(client); 
    } 

    @Override 
    public SecuredRestAdapter setClient(Provider clientProvider) { 
     client = clientProvider.get(); 
     return (SecuredRestAdapter) super.setClient(clientProvider); 
    } 

    @Override 
    public SecuredRestAdapter setErrorHandler(ErrorHandler errorHandler) { 

     return (SecuredRestAdapter) super.setErrorHandler(errorHandler); 
    } 

    @Override 
    public SecuredRestAdapter setExecutors(Executor httpExecutor, 
              Executor callbackExecutor) { 

     return (SecuredRestAdapter) super.setExecutors(httpExecutor, 
       callbackExecutor); 
    } 

    @Override 
    public SecuredRestAdapter setRequestInterceptor(
      RequestInterceptor requestInterceptor) { 

     return (SecuredRestAdapter) super 
       .setRequestInterceptor(requestInterceptor); 
    } 

    @Override 
    public SecuredRestAdapter setConverter(Converter converter) { 

     return (SecuredRestAdapter) super.setConverter(converter); 
    } 

    @Override 
    public SecuredRestAdapter setProfiler(@SuppressWarnings("rawtypes") Profiler profiler) { 

     return (SecuredRestAdapter) super.setProfiler(profiler); 
    } 

    @Override 
    public SecuredRestAdapter setLog(Log log) { 

     return (SecuredRestAdapter) super.setLog(log); 
    } 

    @Override 
    public SecuredRestAdapter setLogLevel(LogLevel logLevel) { 

     return (SecuredRestAdapter) super.setLogLevel(logLevel); 
    } 

    public SecuredRestAdapter setToken(String token) { 
     this.token = token; 
     return this; 
    } 

    @Override 
    public RestAdapter build() { 
     if (this.token == null || this.token.equals("")) 
      throw new SecuredRestAdapterException(
        "Token must be provided, when calling SecuredRestAdapter"); 

     if (client == null) { 
      client = new OkClient(); 
     } 
     OAuthHandler hdlr = new OAuthHandler(client, token); 
     setRequestInterceptor(hdlr); 

     return super.build(); 
    } 
} 

Exception类:

public class SecuredRestAdapterException extends RuntimeException { 
    public SecuredRestAdapterException(String message) { 
     super(message); 
    } 
}