Pull to refresh

Как обновить статус в Facebook, LinkedIn & Twitter при помощи spring-social

Reading time9 min
Views6.7K
Не так давно я уже писал о новом проекте SpringSource: spring-social. Сегодня я хочу рассказать (вернее показать на примере) как можно данную библиотеку использовать. В качестве примера используется простейшее приложение, которое позволяет залогиниться во все три сервиса и обновлять статус одновременно во всех трех (осторожно — под катом много букв и кода и совсем нет картинок).
imageimageimageimage

Обновление статуса было взято в качестве примера потому что:
* это просто и понятно;
* эта функция не поддерживается spring-social для LinkedIn-а, то есть можно посмотреть как добавлять реализацию APIшых методов сервисов, не реализованных в spring-social по умолчанию.
В принципе на месте обновления статуса может быть любая другая функциональность.

Сами разработчики советуют смотреть в качестве примера использования библиотеки их приложение GreenHouse — но, приложение достаточно большое и вычленить имено «социальную» часть не так легко. Было так же опубликовано пара примеров — но достаточно примитивных — не дающих «полной картины». Потому я и решил написать небольшой пример (самому разобраться и другим показать) который бы с одной стороны представлял с собой некоторое законченный пример от логина в сервис до отправки статуса, а с другой стороны был бы максимально простым. Потому — извините что html голый без каких либо стилей — плюс java-код не везде «красив» — в некоторых местах код можно было бы сделать «правильней» например путем наследования, но это бы «зымылило» бы пример — пришлось бы разбираться еще с дополнительной иерархией классов.

Данный пост является вольным переводом моего англоязычного поста.
Все исходники доступны в svn: http://svn.emforge.net/svnroot/akakunin-experiments/update-status
Исходники так же можно просмотреть на сайте EmForge

Базовое приложение


В качестве «базы» мы используем простейшее приложение на spring framework и spring-mvc. В данном приложении используется стандартный джентльменский набор:
* Maven для сборки;
* Spring Framework 3.0.5;
* Spring-Security 3.0.5;
* JPA (через Hibernate);
* HSQL в качестве базы.

В принципе все максимально стандартно и просто. Есть единственная Entity — net.emforge.updatestatus.entity.User — которая хранит пользователей (userName, password). Для каждого пользователя так же сохраняются токены, которые используются для работы с каждым из сервисов.
Есть DAO-класс UserDao, и сервис UserServiceImpl.
В приложении целых две странички — главная (index) с формой для логина и регистрации (обрабатывается UserController-ом), и страничка status доступная только залогиненым пользователям — о ней мы поговорим позже.

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

Регистрация приложения


Что бы данный пример работал, вам будет необходимо сначала зарегистрировать свое приложение в тех сервисах, которые вы собираетесь использовать:
* Регистрация на Facebook-е осуществляется на странице http://developers.facebook.com/setup/ — только просьба использовать в качестве SiteURL localhost:8080/update-status/ — иначе callback работать не будет;
* Регистрация в Twitter: http://dev.twitter.com/apps/new;
* Регистрация в LinkedIn: https://www.linkedin.com/secure/developer

В каждом случае вы получите два ключа — ключ приложения (или id приложения) & security-token — их надо сохранить в src/main/resources/config.properties

Запустить по быстрому


Если вы хотите по быстрому посмотреть как это работает и у вас есть java & maven (я надеюсь что это так :) ) — то просто возьмите исходники из svn и выполните (после того как пропишите свои ключи в config.properties)
mvn tomcat:run

После чего приложение должно быть доступно по адресу http://localhost:8080/update-status
Создайте нового пользователя — и вперед, а мы посмотрим как же это все реализовано

Коннект в LinkedIn и Twitter


Для работы с обоими сервисами используется OAuth и процедура соединения аналогична — так что покажу на примере twitter-а.
Для работы с сервисом пишем простейший класс TwitterProvider главный метод в котором — getOAuthService:
public OAuthService getOAuthService() {
  OAuthConfig config = new OAuthConfig();
    config.setRequestTokenEndpoint("https://api.twitter.com/oauth/request_token");
    config.setAccessTokenEndpoint("https://api.twitter.com/oauth/access_token");
    config.setAccessTokenVerb(Verb.POST);
    config.setRequestTokenVerb(Verb.POST);
    config.setApiKey(apiKey);
    config.setApiSecret(apiSecret);
    config.setCallback(callbackUrl);
  
    return new OAuth10aServiceImpl(
        new HMACSha1SignatureService(),
        new TimestampServiceImpl(),
        new BaseStringExtractorImpl(),
        new HeaderExtractorImpl(),
        new TokenExtractorImpl(),
        new TokenExtractorImpl(),
        config);
  }


apiKey, apiSecret & callbackUrl инжектятся из config.properties (куда мы из сначала прописываем).

Для коннекта добавляем ссылку /connet/twitter и вешаем обработчик (в SocialController):
  @RequestMapping(value = "/connect/twitter", method = RequestMethod.GET)
  public String requestConnectionToTwitter(WebRequest request) {
    // get request token
    Token requestToken = twitterProvider.getOAuthService().getRequestToken();
    // store request token in session
    request.setAttribute("twitter_request_token", requestToken, WebRequest.SCOPE_SESSION);
   
    return "redirect:" + twitterProvider.getAuthorizeUrl() + "?oauth_token=" + requestToken.getToken();
  }



По сути дела данный обработчик делает редирект на Twitter для прохождения логина в нем. В случае успешного логина Twitter сделает редирект на callbackUrl и передаст туда ключи пользователя. Повесим в SocialController обработчик для callBack-а:
  /** Callback from twitter on success login
   *
   * @param verifier
   * @param request
   * @return
   */
  @RequestMapping(value = "/callback/twitter", method = RequestMethod.GET, params = "oauth_token")
  public String authorizeTwitterCallback(@RequestParam(value = "oauth_verifier", defaultValue = "verifier") String verifier,
                  WebRequest request) {
    // get request token from session
    Token requestToken = (Token)request.getAttribute("twitter_request_token", WebRequest.SCOPE_SESSION);
   
    // get access token
    Token accessToken = twitterProvider.getOAuthService().getAccessToken(requestToken, new Verifier(verifier));
    String userName = getCurrentUser().getName();
    userService.updateTwitterAuthentication(userName, accessToken.getToken(), accessToken.getSecret());
   
    return "redirect:/status";
  
  }

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

Коннект в Facebook


В Facebook-ом все немного хитрее. Для того что бы законнектиться в Facebook мы размещаем на нашу страничку status.jsp кнопку «Connect to Facebook» используя следующий код:
<form id="fb_signin" action="<c:url value="/connect/facebook" />" method="post">
  <div class="formInfo">
  </div>
  <div id="fb-root"></div>  
  <p><fb:login-button perms="email,publish_stream,offline_access" onlogin="$('#fb_signin').submit();" v="2" length="long">Connect to Facebook</fb:login-button></p>
</form>

<facebook:init />


Тут важно обратить внимание на следующие моменты:
* facebook:init тег реализован в библиотеке spring-social и ему требуется, что бы в спринговом контексте был бин ${facebookProvider} — у нас он реализован в классе FacebookProvider. По сути дела из этого бина тег берет только ключи;
* fb:login-button генерит кнопку «Connect to Facebook»;
* form-action /connect/facebook будет использован в качестве callback-а в случае успешного выполнения логина.

Так же для того что бы этот код заработал необходимо подключить tagLib
<%@ taglib uri="http://www.springframework.org/spring-social/facebook/tags" prefix="facebook" %>

и подключить jQuery — в моем случае я просто сделал



Ну и надо написать обработчик callback-а, который придет на /connect/facebook:
  @RequestMapping(value="/connect/facebook", method=RequestMethod.POST)
  public String connectAccountToFacebook(@FacebookAccessToken String accessToken,
                      @FacebookUserId String facebookUserId) {
    if (facebookUserId != null && accessToken != null) {
      // store facebook information
      String userName = getCurrentUser().getName();
      userService.updateFacebookAuthentication(userName, accessToken, facebookUserId);     
    }
    return "redirect:/status";
  }

* This source code was highlighted with Source Code Highlighter.


В данном примере мы увидим две интересные аннотации: @FacebookAccessToken и @FacebookUserId — это одна из фичек spring-social, но что бы она работала, нам необходимо добавить в проект специальный WebArgResolver. Для этого добавляем в applicationContext.xml:
    <bean id="facebookWebArgResolver" class="org.springframework.social.facebook.FacebookWebArgumentResolver">
      <constructor-arg name="apiKey" value="${facebook.appId}"/>
    </bean>

* This source code was highlighted with Source Code Highlighter.

Обработка callback-а аналогична Tiwtter & Linkedin — сохраняем ключи (в нашем случае facebookUserId & facebookToken) для дальнейшего использования.

Отправка статуса в Twitter & Facebook


Законнектиться-законнектились, теперь можно и статус обновлять. На страничке статус у нас есть textArea где пользователь вводит текст и отправляет сообщение. Обработчик формы выглядит следующим образом:
  @RequestMapping(value = "/status", method = RequestMethod.POST)
  public String sendStatus(@Valid StatusForm statusForm, BindingResult result, ModelMap modelMap) {
    User user = getCurrentUser();
   
    LinkedInTemplateExt linkedInTemplate = linkedInProvider.createTemplate(user);
    FacebookTemplate facebookTemplate = facebookProvider.createTemplate(user);
    TwitterTemplate twitterTemplate = twitterProvider.createTemplate(user);
   
    // send message to LinkedIn
    if (linkedInTemplate != null) {
      linkedInTemplate.updateStatus(statusForm.getStatus());
    }
   
    // send message to Facebook
    if (facebookTemplate != null) {
      facebookTemplate.updateStatus(statusForm.getStatus());
    }
   
    // send message to Twitter
    if (twitterTemplate != null) {
      twitterTemplate.updateStatus(statusForm.getStatus());
    }
    return "redirect:/status";
  }

* This source code was highlighted with Source Code Highlighter.

В данном обработчике мы получаем FacebookTemplate & TwitterTemplate (оба класса из spring-social) используя ключи текущего пользователя и вызываем их метод updateStatus

Отправка статуса в LinkedIn


С LinkedIn-ом получилось немного сложней — тот LinkedInTemplate, которые реализован в spring-social не содержит метода для обновления статуса — так что нам придется написать его самим (используя REST-API вызов api.linkedin.com/v1/people/~/person-activities). Для этого мы пишем класс LinkedInTemplateExt — наследник «оригинального» LinkedInTemplate в котором:
* Инициализируем собственный объект класса RestOperations — при этом используется специальный OAuth1RequestSignerFactory.getRequestSigner для «правильного» кодирования вызовов (еще одна фича spring-social);
* Реализуем сам метод updateStatus:
  public void updateStatus(String message) {
    LinkedInPersonActivity personActivity = new LinkedInPersonActivity(message);
   
    restOperationsExt.postForLocation("http://api.linkedin.com/v1/people/~/person-activities", personActivity);
  }

* This source code was highlighted with Source Code Highlighter.


нам так же потребуется написать класс LinkedInPersonActivity — для передачи статуса:
@XmlRootElement(name = "activity")
public class LinkedInPersonActivity {
  public LinkedInPersonActivity() {
  }
  public LinkedInPersonActivity(String body) {
    this.body = body;
  }
 
  @XmlElement(name = "content-type")
  String contentType = "linkedin-html";
 
  @XmlElement(name = "body")
  String body;
}

* This source code was highlighted with Source Code Highlighter.


Все — метод готов — как видно — не смотря на то что LinkedInTemplate изначально поддерживает очень мало функций из API — добавление новых функций не такая уж и сложная задача.

Вот вроде и все — надеюсь этот пример окажется полезным.
Tags:
Hubs:
Total votes 11: ↑6 and ↓5+1
Comments7

Articles