Давненько я ничего не писал про Google API в Delphi, а между тем, разработчики из Google выпустили экспериментальный API, позволяющий нам без особых хлопот получить токен для авторизации в сервисах с помощью OAuth. Ранее OAuth использовался только для web-приложений.
Авторизация в сервисах Google теперь стала напоминать, что-то наподобие OAuth-авторизации в Twitter, т.е. вначале получаем код доступа, затем меняем его на access_token, который в дальнейшем и используем для работы с ресурсами какого-либо сервиса.
Рассмотрим пример авторизации в сервисе Google с помощью OAuth.
1. Регистрация клиентского приложения.
Прежде всего нам необходимо зарегистрировать наше приложение в Google. Для этого необходимо перейти по ссылке. Нажать большую синюю кнопку:
И в открывшейся форме задать необходимые параметры клиентского приложения. Для настольных приложений необходимо в разделе “Application Type” указать “Installed application”:
Жмем “Create client ID” и Google выдаст для нашего клиента три параметра:
- Client ID – id нашего клиента
- Client secret – секретный ключ нашего приложения
- Redirect URIs – строка, содержащая URL для редиректа. Для настольных приложений строка всегда имеет вид: urn:ietf:wg:oauth:2.0:oob
Теперь, используя эти параметры мы можем получать доступ к ресурсам API Google. Создадим небольшой класс для авторизации.
2. Класс для OAuth-авторизации в Google
Итак, назовем наш класс – TOAuth. Какие свойства он должен содержать? Во-первых, это параметры нашего клиентского приложения – Client ID и Client Secret. Т.к. Redirect URLs никогда не меняется, то зададим это значение как константу.
Далее, согласно спецификации OAuth токен мы будем получать на определенный ресурс, т.е. мы должны предусмотреть хранение название этого ресурса в объекте, назовем свойство как это принято – Scope.
Таким образом, заготовка для модуля нашего будущего класса OAuth будет выглядеть следующим образом:
const redirect_uri='urn:ietf:wg:oauth:2.0:oob'; type TOAuth = class private FClientID: string;//id клиента FClientSecret: string;//секретный ключ клиента FScope : string;//точка доступа procedure SetClientID(const Value: string); procedure SetScope(const Value: string); public constructor Create; destructor destroy; override; property ClientID: string read FClientID write SetClientID; property Scope : string read FScope write SetScope; property ClientSecret: string read FClientSecret write FClientSecret; end;
Теперь обратимся к документации Google и посмотрим как получается значение токена. На первом шаге нам необходимо сформировать URL, перейдя по которому пользователь введет в форме свой логин и пароль и подтвердит, что он предоставляет доступ нашему клиенту к ресурсам своего аккаунта. В ответ Google выдаст пользователю код доступа, который должен быть передан в наш класс. Этот код мы и поменяем у Google на действующий в течение часа токен. Реализуем этот алгоритм в нашем классе. Вначале пишем функцию, которая будет возвращать нам URL для перехода пользователя:
function TOAuth.AccessURL: string; begin Result:=Format(oauth_url,[ClientID,redirect_uri,Scope]); end;
Здесь oath_url — это константа вида:
oauth_url = 'https://accounts.google.com/o/oauth2/auth?client_id=%s&redirect_uri=%s&scope=%s&response_type=code';
После того, как пользователь перейдет по этому URL он увидит, что-то типа:
В “More Info”, кстати, находится сведения о разработчике. Ничего сверхсекретного, никаких адресов сайтов типа http://core-2.ru, исключительно e-mail для связи с разработчиком приложения. Нажав “Allow access” пользователь получит код:
Этот код пользователь должен скопировать и передать нашему клиенту. Добавим в класс новое свойство – ResponseCode, которое будет хранить этот код.
Следующим шагом будет отправка этого кода в Google и получение токена. Отправка производится обычным образом – через POST-запрос. Ответом будет служить JSON-объект, содержащий необходимые нам данные. Пишем вначале метод отправки запроса и получение ответа в виде строки:
function TOAuth.SendRequest(URL: string; Params: TStream): string; var Data: TStringStream; begin if Params=nil then Exit; with THTTPSend.Create do begin MimeType:='application/x-www-form-urlencoded'; Document.LoadFromStream(Params); if HTTPMethod('POST',URL) then begin Data:=TStringStream.Create(); try Data.LoadFromStream(Document); Result:=Data.DataString; finally Data.Free; end; end else Result:=''; end; end;
Теперь мы можем, используя метод SendRequest отправить запрос и получить в ответ текст в формате JSON, содержащий необходмые параметры, а именно:
- acess_token — токен доступа к ресурсу
- expires_in — время жизни токена
- refresh_token — токен для обноления access_token.
Чтобы достать значения этих параметров из ответа сервера напишем следующую функцию:
function TOAuth.ParamValue(ParamName, JSONString: string): string; const StripChars : set of char = ['"',':',',']; var i,j:integer; begin i:=pos(LowerCase(ParamName),LowerCase(JSONString)); if i>0 then begin for j:= i+Length(ParamName) to Length(JSONString)-1 do if not (JSONString[j] in StripChars) then Result:=Result+JSONString[j] else if JSONString[j]=',' then break; end else Result:=''; end;
Функция возвращает значение параметра из документа JSONString по его имени ParamName. Теперь добавляем в наш класс необходимые свойства:
property Access_token: string read FAccess_token write SetAccess_token; property Expires_in: string read FExpires_in write SetExpires_in; property Refresh_token:string read FRefresh_token write SetRefresh_token;
И пишем процедуру получения токена по коду, полученному от пользователя:
function TOAuth.GetAccessToken: string; const tokenurl='https://accounts.google.com/o/oauth2/token'; tokenparams = 'client_id=%s&client_secret=%s&code=%s&redirect_uri=%s&grant_type=authorization_code'; var Params: TStringStream; Response:string; begin Params:=TStringStream.Create(Format(tokenparams,[ClientID,ClientSecret,ResponseCode,redirect_uri])); try Response:=SendRequest(tokenurl,Params); Access_token:=ParamValue('access_token',Response); Expires_in:=ParamValue('expires_in',Response); Refresh_token:=ParamValue('refresh_token',Response); Result:=Access_token; finally Params.Free; end; end;
Функция возвращает Access_Token и присваивает значения свойствам класса. Соответственно, если нам необходимо обновить токен, то метод обновления может выглядеть так:
function TOAuth.RefreshToken: string; const crefreshtoken = 'client_id=%s&client_secret=%s&refresh_token=%s&grant_type=refresh_token'; var Params: TStringStream; Response: string; begin Params:=TStringStream.Create(Format(crefreshtoken,[ClientID,ClientSecret,Refresh_token])); try Response:=SendRequest(tokenurl,Params); Access_token:=ParamValue('access_token',Response); Expires_in:=ParamValue('expires_in',Response); Result:=Access_token; finally Params.Free; end; end;
Теперь остается продемонстрировать работу класса на каком-нибудь примере.
2. Пример использования класса TOAuth.
Для примера, получим доступ к календарям Google. Создадим новое приложение как показано на рисунке:
Справа на форме расположен TWebBrowser в котором мы будем показывать пользователю страницу для авторизации и предоставления доступа.
Пишем обработчик события для кнопки “Получить код”:
var ... OAuth: TOAuth; implementation procedure TForm2.Button2Click(Sender: TObject); begin OAuth:=TOAuth.Create; OAuth.ClientID:=Edit1.Text; OAuth.ClientSecret:=Edit2.Text; OAuth.Scope:=Edit3.Text; WebBrowser1.Navigate(OAuth.AccessURL); end;
Теперь обработчик для кнопки «Получить токен»:
procedure TForm2.Button1Click(Sender: TObject); begin OAuth.ResponseCode:=Edit4.Text; Memo1.Lines.Add('Access Token = '+OAuth.GetAccessToken); Memo1.Lines.Add('Eexpires_in = '+OAuth.Expires_in); Memo1.Lines.Add('Refresh Token = '+OAuth.Refresh_token); end;
и для кнопки «Обновить токен»:
procedure TForm2.Button3Click(Sender: TObject); begin Memo2.Lines.Add(OAuth.RefreshToken) end;
Теперь можно запустить приложение и убедиться, что мы можем авторизоваться в сервисе и получить токен для дальнейшей работы. Точка доступа к календарям: https://www.google.com/calendar/feeds/default/owncalendars/full
Теперь попробуем получить какую-нибудь информацию от сервиса календарей, используя полученный токен. Для примера попросим Google выдать нам список календарей пользователя.
Как и в случае с ClientLogin токен будем передавать в заголовках. Функция получения XML-документа со списком календарей может выглядеть следующим образом:
procedure TForm2.Button4Click(Sender: TObject); var HTTP:THTTPSend; begin HTTP:=THTTPSend.Create; try HTTP.Headers.Add('Authorization: OAuth '+OAuth.Access_token); HTTP.Headers.Add('GData-Version: 2'); if HTTP.HTTPMethod('GET','https://www.google.com:443/calendar/feeds/default/allcalendars/full') then Memo2.Lines.LoadFromStream(HTTP.Document); finally HTTP.Free; end; end;
При выполнении этой процедуры Google, как это у него водится, вернет вначале вам перенаправление, поэтому запрос надо будет повторить дважды — вначале как показано выше в листинге, а во втором случае с теме же заголовками, но с URL, который вернет Google. После этого Вы получите в распоряжение XML-документ, содержащий список календарей пользователя.
Книжная полка
Описание: Рассмотрены практические вопросы по разработке клиент-серверных приложений в среде Delphi 7 и Delphi 2005 с использованием СУБД MS SQL Server 2000, InterBase и Firebird. Приведена информация о теории построения реляционных баз данных и языке SQL. Освещены вопросы эксплуатации и администрирования СУБД.
|
||
Название: О чем не пишут в книгах по Delphi
Описание: Рассмотрены малоосвещенные вопросы программирования в Delphi. Описаны методы интеграции VCL и API. Показаны внутренние механизмы VCL и приведены примеры вмешательства в эти механизмы. Рассмотрено использование сокетов в Delphi: различные режимы их работы, особенности для протоколов TCP и UDP и др.
|
if HTTPMethod(‘POST’,URL) then — возвращает false!
[…] был взят класс для работы с Google OAuth о котором я рассказывал в блоге некоторое время назад, но со следующими […]
[…] несколько мануалов по OAuth 2.0. Ну или прочитать пост «Google API в Delphi. OAuth для Delphi-приложений» в моем блоге, чтобы иметь хотя бы небольшое […]