Статьи

Обучение Android: аутентификация с помощью Twitter с использованием OAuth

Я хочу иметь возможность получать твиты из моей временной шкалы в свое приложение, а это значит, что мне нужно авторизовать приложение с помощью Twitter с помощью OAuth.

Последний раз, когда я пытался пройти проверку подлинности с использованием OAuth пару лет назад, это был провал, но, к счастью, на этот раз Хонза Покорный написал сообщение в блоге, объясняющее, что делать .

Мне пришлось немного откорректировать код по сравнению с тем, что написано на его посту, поэтому я решил документировать то, что сделал.

Мы используем библиотеку указателей, для которой нам нужно скачать следующие два jar-файла и поместить их в каталог libs приложения.

wget http://oauth-signpost.googlecode.com/files/signpost-commonshttp4-1.2.1.1.jar
wget http://oauth-signpost.googlecode.com/files/signpost-core-1.2.1.1.jar

 

Я создал кнопку, которую нужно было нажать, чтобы запустить первый шаг аутентификации OAuth через твиттер. Код выглядит так:

public class MyActivity extends Activity {
  private String CALLBACKURL = "app://twitter";
  private String consumerKey = "TwitterConsumerKey";
  private String consumerSecret = "TwitterConsumerSecret";
 
  private OAuthProvider httpOauthprovider = new DefaultOAuthProvider("https://api.twitter.com/oauth/request_token", "https://api.twitter.com/oauth/access_token", "https://api.twitter.com/oauth/authorize");
  private CommonsHttpOAuthConsumer httpOauthConsumer = new CommonsHttpOAuthConsumer(consumerKey, consumerSecret);
 
  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
 
    ImageButton oauth = (ImageButton) findViewById(R.id.oauth_button);
    oauth.setOnClickListener(new View.OnClickListener() {
      public void onClick(View v) {
        try {
          String authUrl = httpOauthprovider.retrieveRequestToken(httpOauthConsumer, CALLBACKURL);
          Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(authUrl));
          v.getContext().startActivity(intent);
        } catch (Exception e) {
          Log.w("oauth fail", e);
          Toast.makeText(v.getContext(), e.getMessage(), Toast.LENGTH_LONG).show();
        }
      }
    });
  }
}

CALLBACK URL называют Twitter , когда пользователь разрешает применение (и , следовательно , запрос маркера). Обычно это HTTP-URL, но в этом случае нам нужно определить специальный URL-адрес, который обрабатывается нашим приложением.

ConsumerKey и consumerSecret является значением , присвоенным Twitter для применения .

Определение URL обратного вызова в файле манифеста выглядит следующим образом:

<activity android:name="MyActivity" android:label="@string/app_name" android:launchMode="singleInstance">
  ...
  <intent-filter>
    <action android:name="android.intent.action.VIEW"/>
    <category android:name="android.intent.category.DEFAULT"/>
    <category android:name="android.intent.category.BROWSABLE"/>
    <data android:scheme="app" android:host="twitter" />
  </intent-filter>
</activity>

 

Мы можем изменить app: // twitter на что угодно, но оно должно соответствовать тому, что определено в элементе данных в нашем файле манифеста.

Я обнаружил, что мне нужно определить URL обратного вызова в настройках моего приложения в Twitter, в противном случае я получил эту ошибку:

oauth.signpost.exception.OAuthCommunicationException: Communication with the service provider failed: https://api.twitter.com/oauth/request_token
        at oauth.signpost.AbstractOAuthProvider.retrieveToken(AbstractOAuthProvider.java:214)
        at oauth.signpost.AbstractOAuthProvider.retrieveRequestToken(AbstractOAuthProvider.java:69)
...
 Caused by: java.io.FileNotFoundException: https://api.twitter.com/oauth/request_token
        at org.apache.harmony.luni.internal.net.www.protocol.http.HttpURLConnectionImpl.getInputStream(HttpURLConnectionImpl.java:521)
        at org.apache.harmony.luni.internal.net.www.protocol.https.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:258)
        at oauth.signpost.basic.HttpURLConnectionResponseAdapter.getContent(HttpURLConnectionResponseAdapter.java:18)
        at oauth.signpost.AbstractOAuthProvider.handleUnexpectedResponse(AbstractOAuthProvider.java:228)
        at oauth.signpost.AbstractOAuthProvider.retrieveToken(AbstractOAuthProvider.java:189)

 

В итоге я просто установил свой URL обратного вызова в Twitter на URL этого блога. Кажется, не имеет значения, какой адрес вы указали, поскольку он все равно будет переопределен нашим URL обратного вызова, но его нужно установить.

Затем обратный вызов обрабатывается следующим кодом в MyActivity

public class MyActivity extends Activity {
  ...
 
  @Override
  protected void onNewIntent(Intent intent) {
    super.onNewIntent(intent);
 
    Log.w("redirect-to-app", "going to save the key and secret");
 
    Uri uri = intent.getData();
    if (uri != null && uri.toString().startsWith(CALLBACKURL)) {
 
        String verifier = uri.getQueryParameter(oauth.signpost.OAuth.OAUTH_VERIFIER);
 
        try {
            // this will populate token and token_secret in consumer
 
            httpOauthprovider.retrieveAccessToken(httpOauthConsumer, verifier);
            String userKey = httpOauthConsumer.getToken();
            String userSecret = httpOauthConsumer.getTokenSecret();
 
            // Save user_key and user_secret in user preferences and return
            SharedPreferences settings = getBaseContext().getSharedPreferences("your_app_prefs", 0);
            SharedPreferences.Editor editor = settings.edit();
            editor.putString("user_key", userKey);
            editor.putString("user_secret", userSecret);
            editor.commit();
 
        } catch (Exception e) {
 
        }
    } else {
        // Do something if the callback comes from elsewhere
    }
  }
}

 

… Который делает еще один вызов в Twitter, чтобы получить ключ и секрет пользователя (токен доступа), который затем сохраняется в общих настройках, чтобы мы могли использовать его в будущем без повторной аутентификации в Twitter.

Затем мы можем запросить Twitter так:

HttpGet get = new HttpGet("http://api.twitter.com/1/statuses/home_timeline.json");
HttpParams params = new BasicHttpParams();
HttpProtocolParams.setUseExpectContinue(params, false);
get.setParams(params);
 
try {
  SharedPreferences settings = getContext().getSharedPreferences("your_app_prefs", 0);
  String userKey = settings.getString("user_key", "");
  String userSecret = settings.getString("user_secret", "");
 
  httpOauthConsumer.setTokenWithSecret(userKey, userSecret);
  httpOauthConsumer.sign(get);
 
  DefaultHttpClient client = new DefaultHttpClient();
  String response = client.execute(get, new BasicResponseHandler());
  JSONArray array = new JSONArray(response);
} catch (Exception e) { 
  // handle this somehow
}

Здесь мы извлекаем ключ и секрет пользователя, которые мы сохранили на предыдущем шаге, а затем устанавливаем их для нашего потребителя OAuth, который мы используем для подписания нашего запроса в Twitter.

Есть хорошее объяснение того, как OAuth работает примерно на половине этого поста в StackOverFlow , у Эрана Хаммера-Лахава есть довольно хорошее «руководство для начинающих по OAuth» в его блоге, и спецификация OAuth также удивительно читаема.

 

С http://www.markhneedham.com/blog/2012/01/02/learning-android-authenticating-with-twitter-using-oauth/