使用OAuth2改进异步调用

时间:2015-11-07 10:28:40

标签: android asynchronous oauth oauth-2.0 retrofit

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

第二个,异步,开箱即用。但是,对于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
    }

1 个答案:

答案 0 :(得分: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();
    }
}

异常类:

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