Библиотека OAuth для Delphi уже не раз рассматривалась в блоге, но до практического её применения дело как-то не доходило. Сегодня я решил исправить ситуацию и рассмотреть применение библиотеки OAuth на примере использования при работе с Twitter API. Причин выбора именно этого API две:
1. Twitter с начала лета планирут отказаться от использования Base-авторизации для доступа к API. Вместо этого разработчикам настольных приложений предлагается использовать либо OAuth с PIN-кодом, либо XAuth.
2. Не так давно я рассматривал в блоге свою программу для сжатия ссылок Link Compressor, которая использует API Twitter’а для публикации сообщений со ссылками. В программе используется Base-авторизация. Поэтому, чтобы после отказа команды Twitter’а от Base-авторизации Link Compressor продолжал нормально функционировать, необходимо применить в её работе OAuth.
Вначале рассмотрим как выглядит алгоритм доступа к API Twitter’а с использованием OAuth.
1. После того как Вы зарегистрируете свое приложение в Twitter Вам выдается два ключа: Consumer key и Consumer secret.
2. Далее Вы должны отправить запрос на адрес http://api.twitter.com/oauth/request_token для получения значений oauth_token, oauth_token_secret.
3. После этого Ваше приложение должно выполнить GET-запрос на http://twitter.com/oauth/authorize. В запросе должны присутствовать полученные в п.2. параметры. Загруженная страница показывается пользователю. Пользователь вводит свой логин и пароль к аккаунту Twitter’а и жмет «Allow» (разрешить доступ).
5. Пользователю будет показан PIN-код, который он должен вернуть в приложение (ввести в Edit или сохранить иным способом в программе).
6. PIN-код отправляется в параметре oauth_verifier на адрес http://twitter.com/oauth/access_token. В результате Twitter вернет нам снова oauth_token, oauth_token_secret. Именно эти значения и будут использоваться в дальнейшем для доступа к API.
Здесь следует отметить что полученные на последнем шаге параметры oauth_token, oauth_token_secret не имеют срока давности. То есть запросив один раз авторизацию в твитере Вы можете в дальнейшем сколь угодно долго использовать полученые значения для доступа к API — главное обеспечить их безопасное хранение в реестре, файле и т.д.
Теперь перейдем к реализации примера. Как следует из алгоритма, для работы нам понадобится каким-либо образом показать пользователю страницу для ввода логина и пароля и получения PIN-кода. Я для этого создал в проекте Link Compressor’а дополнительную форму:
На форме содержится Edit для ввода PIN-кода, WebBrowser для отображения страницы для авторизации и PIN-кода, CheckBox для того, чтобы можно было сохранить параметры авторизации и кнопка TButton клик по которой завершит процесс авторизации. Для хранения и использования данных API твитера будем использовать следуюий класс:
type TTwitter = class(TObject) private FAuthorized: boolean; FSource: string; FTwitterClient: string; FTwitterClientVersion: string; FTwitterClientURL: string; FUserName: string; FPassword: string; FConsumer: TOAuthConsumer; FToken: TOAuthToken; FRequest: TOAuthRequest; FHMAC: TOAuthSignatureMethod_HMAC_SHA1; FKey: string; FSecret: string; FOAuth_token: string; FOAuth_token_secret: string; FAuthHeader: string; procedure SetSource(const Value: string); procedure SetTwitterClient(const Value: string); procedure SetTwitterClientVersion(const Value: string); procedure SetTwitterCLientURL(const Value: string); procedure AddCustomHeader(Sender: TObject; const Method: String; Headers: TStrings); procedure CheckResponce(const Responce:string); public constructor Create; function POSTCommand(URL: string; Params: TStringList): string; function GETCommand(URL: string): string; function Update(const Text: string): string; property Source: string read FSource write SetSource; property TwitterClient: string read FTwitterClient write SetTwitterClient; property TwitterClientVersion: string read FTwitterClientVersion write SetTwitterClientVersion; property TwitterClientURL: string read FTwitterClientURL write SetTwitterCLientURL; property Consumer: TOAuthConsumer read FConsumer write FConsumer; property Token: TOAuthToken read FToken write FToken; property Request: TOAuthRequest read FRequest write FRequest; property HMAC: TOAuthSignatureMethod_HMAC_SHA1 read FHMAC write FHMAC; property Key: string read FKey write FKey; property Secret: string read FSecret write FSecret; property OAuth_token: string read FOAuth_token write FOAuth_token; property OAuth_token_secret : string read FOAuth_token_secret write FOAuth_token_secret; property Authorized: boolean read FAuthorized write FAuthorized; end;
Я не буду сейчас подробно останавливаться на работе этого класса (рассмотрю только один метод), т.к. теже методы GETCommand и POSTCommand Вы можете посмотреть в этом посте, а такие методы как SetTwitterClientVersion, SetSource и пр. просто присваивают значения полям класса.
Теперь надо определиться с тем, как будет вызываться форма. Т.к. я внедряю OAuth в уже разработанную программу, то я решил пойти уже проложенным путем и начинать процесс авторизации с окна настроект программы:
По клику на кнопке «Авторизоваться» происходит первая часть авторизации, включающая в себя пункты 1 и 2 алгоритма и вызов формы авторизации.
Вначале о том как получаются первые значения oauth_token, oauth_token_secret. Листинг процедуры:
procedure StartAuthorization(var LoginURL: string); var URL, Tok: string; endpos: integer; Response: TStringList; Stream: TStringStream; HTTP: THTTPCli; begin try //создаем необходимые объекты //Key = Consumer Key; Secret = Consumer Secret Twitter.Consumer := TOAuthConsumer.Create (Twitter.Key, Twitter.Secret); Twitter.HMAC := TOAuthSignatureMethod_HMAC_SHA1.Create; //URL куда будем отправлять данные URL := 'http://twitter.com/oauth/request_token'; //создаем все необходимые параметры запроса Twitter.Request := TOAuthRequest.Create(URL); Twitter.Request := Twitter.Request.FromConsumerAndToken (Twitter.Consumer, nil, URL); Twitter.Request.Sign_Request(Twitter.HMAC, Twitter.Consumer, nil); //добавляем полученные параметры к URL URL := URL + '?' + Twitter.Request.GetString; //отправляем данные (используется ICS) Stream := TStringStream.Create; HTTP := THTTPCli.Create(nil); HTTP.RcvdStream := Stream; HTTP.URL := URL; HTTP.Get; Response := TStringList.Create; Response.Text := Stream.DataString; //анализируем ответ Tok := Trim(Response.Text); endpos := AnsiPos('&oauth_token_secret=', Tok); Twitter.OAuth_token := ''; Twitter.OAuth_token_secret := ''; Twitter.OAuth_token := Copy(Tok, 13, endpos - 13); Tok := Copy(Tok, endpos, Length(Tok)); Twitter.OAuth_token_secret:= Copy(Tok, 21, Length(Tok)); Twitter.Token := TOAuthToken.Create(Twitter.OAuth_token, Twitter.OAuth_token_secret); //генерируем URL для авторизации пользователя URL := 'http://twitter.com/oauth/authorize'; LoginURL := URL + '?' + 'oauth_token=' + Twitter.OAuth_token + '&' + 'oauth_token_secret=' + Twitter.OAuth_token_secret + '&oauth_callback=' + TOAuthUtil.urlEncodeRFC3986(CallbackURL); finally FreeAndNil(Response); FreeAndNil(HTTP); FreeAndNil(Stream); end; end;
Соответственно после того как сгенерирован URL авторизации Вы можете прямо здесь же в процедуре вызвать форму с WebBrowser’ом, например так:
WB.Navigate(LoginURL); Form.ShowModal;
Первая часть завершена. Теперь пользователь вводит логин и пароль вот в такую форму:
после чего вводит полученый PIN-код в Edit и жмет кнопку. Приступаем к финальной стадии авторизации — получению параметров OAuth для использования при доступе к API. Процедура выглядит следующим образом:
function Authorization(const PinCode: string): string; var URL, Tok: string; endpos: integer; Response: TStringList; Stream: TStringStream; HTTP: THTTPCli; begin try URL := 'http://twitter.com/oauth/access_token'; Twitter.Consumer := nil; Twitter.Consumer := TOAuthConsumer.Create (Twitter.Key, Twitter.Secret, CallbackURL); Twitter.Request.HTTPURL := URL; Twitter.Request := Twitter.Request.FromConsumerAndToken (Twitter.Consumer, Twitter.Token, URL); Twitter.Request.Sign_Request(Twitter.HMAC, Twitter.Consumer, Twitter.Token); //добавляем PIN-код в запрос URL := URL + '?' + Twitter.Request.GetString + '&oauth_verifier=' + PinCode; Response := TStringList.Create; Stream := TStringStream.Create; HTTP := THTTPCli.Create(nil); HTTP.RcvdStream := Stream; HTTP.URL := URL; HTTP.Get; Response.Text := Stream.DataString; Tok := Trim(Response.Text); endpos := AnsiPos('&oauth_token_secret=', Tok); Twitter.OAuth_token := ''; Twitter.OAuth_token := Copy(Tok, 13, endpos - 13); Tok := Copy(Tok, endpos, Length(Tok)); endpos := AnsiPos('&user', Tok); Twitter.OAuth_token_secret := ''; Twitter.OAuth_token_secret := Copy(Tok, 21, endpos - 21); Result := Twitter.OAuth_token_secret; finally FreeAndNil(HTTP); FreeAndNil(Stream); FreeAndNil(Response); end; end;
Теперь процесс авторизации можно считать завершенным и можно приступать к работе с API. Отправим новое сообщение в Твитер. Для этого используется метод Update класса TTwitter:
function TTwitter.Update(const Text: string): string; var TextPost, URL: string; Data: TStringList; begin URL := 'http://api.twitter.com/1/statuses/update.xml'; Data := TStringList.Create; TextPost := Format('status=%s', [Text]); Data.Add(TextPost); Result := POSTCommand(URL, Data); Data.Free; end;
Как видите здесь уже нет логина и пароля пользователя. Вместо этого генерируется подпись OAuth и отправляется в заголовках вместе с запросом на сервер.
Осталось только сказать, что в примере использовалась версия библиотеки, работющая с ICS, которую Вы можете скачать здесь и предложить Вам скачать обновленную версию Link Compressor’а для того, чтобы Вы могли увидеть действие библиотеки «вживую». Кстати, если скачаете программу можете обратить внимание, что все Ваши твиты, отправленные с помощью программы будут подписываться как отправленные с помощью программы LinkCompressor.
А вот и ссылка на программу:
[download id=»56″ format=»1″]
hello,
nice work. perhaps you can help me with the modified twitter.pas. especially the procedures addcustomheader and checkresponce. i do not find any hint how to do it. so it would be very nice if you can post it here.
thank you in advance.
Здравствуйте!
Вы не могли бы приложить архив с файлами полного работающего примера?
Той же простой авторизации.
А то никак не пойму как сделать рабочий вариант и повозиться с им вживую
Здравствуйте!
Повторил форму как у вас на скриншоте и добавил обработчик
procedure TForm1.Button1Click(Sender: TObject);
begin
label2.caption:=Authorization(edit1.text);
Memo2.Lines.Text:=’followers:’+twitter.GetUserFollowers(json);
Memo3.Lines.Text:=’friends:’+twitter.GetFriends(json);
end;
procedure TForm1.FormCreate(Sender: TObject);
var loginurl,url:string;
begin
Twitter := TTwitter.Create;
Twitter.Key := ‘xCMWOSqX06aEcbZEDeE6yQ’;
Twitter.Secret := ‘evVCYDouqf4bd9aIDlnpwgiTzyT6totm37Ggqey1pPI’;
CallbackURL:=’http://tigrazone.narod.ru’;
StartAuthorization(loginurl);
WebBrowser1.Navigate(LoginURL);
end;
при нажатии на кнопку войти выводиться должны список followes и friends
но ошибка forbidden и ничего не происходит
неавторизован пишет
что я делаю не так и как сделать чтоб работало?
В библиотеки есть утечка памяти — не хватает деструктора на уничтожение FParameters:
destructor TOAuthRequest.Destroy;
begin
FreeAndNil(Self.FParameters);
inherited Destroy;
end;